/**
 * Description: WebAudio Sequencer of Synthesizer.
 *
 * Functions for GUI:
 *   drawTimeScale()
 *   drawTimeScaleBeats(offset, lengthMeasure, lengthBeat)
 *   drawRangeNotes()
 *   drawNotesStrip()
 *   drawVexflow(clef)
 *   drawHeaderInfo()
 *   drawTrackInfo()
 *   drawNotesSong()
 *   drawNote(ch, note, offset, delta)
 *   viewHelp()

 * Function of services:
 *   ptrTimeDragDrope(e)
 *   selTrack(id)
 *   speaker_on_off(id)
 *   btnScale(id)
 *
 * Functions checking, formatting, retrieving data:
 *   durationNote(lengthNote,sub_item)
 *
 * @package     midi-Synth
 * @category    module js
 * @author      Oleg Klenitsky <https://adminkov.bcr.by/midi/>
 * @copyright   2024 Oleg Klenitsky
 * @license     GPL-2.0-or-later
 */

"use strict";

const svgns = "http://www.w3.org/2000/svg";

var midiFile;

var errMidi = [];

var scale = 1;

var ptrTimeIsDragging = false;
var ptrTimeStartX, ptrTimeInitLeft;

var ptrTimeStaveIsDragging = false;
var ptrTimeStaveStartX, ptrTimeStaveInitLeft;

/**
 * Working with changed data.
 * @type {Boolean}
 */
var changedData = false;

/**
 * [changedDataParams description]
 * @type {Object}
 */
var changedDataParams = {
  scrollTop: 230,
  scrollLeft: 0
};

var cnvScale;
var cnvRange;
var cnvBody;
var svgBody;

var groupBorder;
var groupBorderDrawing = false;
var groupBorderStartX, groupBorderStartY;
var groupBorderCorner;
var selNotesCash = [];

var ctxScale;
var ctxRange;
var ctxBody;

/**
 * Purpose: revers of arrNotes (name of notes).
 * Used: drawRangeNotes(), drawNotesStrip(), testNotesStrip().
 * Source: synth.arrNotes
 * @type {Array}
 */
var arrRevers = [];

/**
 * Purpose: draw rects of notes on cnvBody by drawNote(ch, note, offset, delta).
 * Used: drawNotesStrip(), testNotesStrip().
 * Source: arrRevers.
 * @type {array}
 */
var arrNotesTrack = [];

/**
 * Purpose: channels for sounding in a synthesizer.
 * Used: speaker_on_off(id).
 * Source: drawTrackInfo().
 * @type {Array}
 */
var arrChannelsPlay = [];

/**
 * Purpose: draw notes on musical staff by drawVexflow(clef).
 * Source: arrMusicalPart, arrMeasures.
 * @type {array}
 */
var arrVexFlow = [];

/**
 * Purpose: create arrVexFlow by drawNotesSong().
 * Source: arrTracksEvents[numTrack].
 * @type {array}
 */
var arrMusicalPart = [];
/**
 * Array of notes with errors from VexFlow
 * @type {Array}
 */
var arrBadNotes = [];

/**
 * Purpose: draw rects of notes on cnvBody by drawNotesSong().
 * Used: dataCheck().
 * Source: midiFile.getTrackEvents(numTrack);
 * @type {array}
 */
var arrTracksEvents = [];

/**
 * Purpose: draw nunMeasures on musical staff by drawVexflow(clef).
 * Used: formatMusicalPart(), dataCheck().
 * Source: arrTimeSignatureNoBPM.
 * @type {array}
 */
var arrMeasures = [];

/**
 * Purpose: Serve to synchronize the movement of ptrTimeStave relative to ptrTime.
 * @type {Array}
 */
var arrMeasuresStave = [];

/**
 * Purpose: draw nunMeasures, lines, flags on cnvScale by drawTimeScale().
 * Used: dataCheck().
 * Source: arrTimeSignature.
 * @type {Array}
 */
var arrTimeSignatureNoBPM = [];

/**
 * Purpose: drawTimeScale()
 * Source: synth.getTimeSignature();
 * @type {array}
 */
var arrTimeSignature = [];

/**
 * arrTrkCh[Channel] = part1_numPart;
 * @type {Array}
 */
var arrTrkCh = [];

/**
 * [arrNotesRecord description]
 * @type {Array}
 */
var arrNotesRecord = [];

/**
 * Tools Sequencer
 * @type {Array}
 */
var arrDraft      = [];
var arrVelocity   = [];
var arrVolume     = [];
var arrPitchBend  = [];
var arrPanpos     = [];
var arrExpression = [];
var arrModulation = [];
var arrSustain    = [];
var arrSostenuto  = [];
var arrSoftPedal  = [];
var arrBalance    = [];

var sumLength  = 0;
var selTrackNum = "";

var nameComposition = "";

var click_sliders = false;
var click_speaker = false;

var clef = "treble";

var rec_cur_tick = 0;

function initArrTools(){
  arrDraft      = [];
  arrVelocity   = [];
  arrVolume     = [];
  arrPitchBend  = [];
  arrPanpos     = [];
  arrExpression = [];
  arrModulation = [];
  arrSustain    = [];
  arrSostenuto  = [];
  arrSoftPedal  = [];
  arrBalance    = [];
}
/**
 * [drawTimeScale description]
 *
 * EVENT_META_SET_TEMPO = 0x51;      //81
 * tempoBPM: 120  - 120 1/4notes per 1 minute (or 120 beats per minute)
 * tempo: 500 000 - 1/4note - 500 000 microseconds
 *
 * EVENT_META_TIME_SIGNATURE = 0x58; //88
 * param1 - time signature numerator
 * param2 - power of 2 - time signature denominator
 * param3 - tiks (24 of 1/4note, 48 of 1/2note)  PPQN
 * param4 - number of 1/32 notes per measure
 */
function drawTimeScale(){
  var ppqn = synth.getPPQN();
  var maxTick = synth.getSongDurationTicks();
  var measure = 0;
  var lengthBeat = 0;

  arrMeasures = [];

  arrTimeSignature = synth.getTimeSignature();

  if(0 == arrTimeSignature.length) return;

  //Draw subMeasures. from arrTimeSignature
  ctxScale.textAlign = "center";
  ctxScale.fillStyle = "red";
  arrTimeSignature.forEach(function(item, idx) {
    if(81 == item.subtype)ctxScale.fillText("o", (item.tick/12)*scale, 17);
  });

  arrTimeSignatureNoBPM = arrTimeSignature.filter(function(item) {
    if (81 == item.subtype) {
      return false;
    }
    if (88 == item.subtype) {
      return true;
    }
  });

  if(0 == arrTimeSignatureNoBPM){
    errMidi.push("no TimeSignature in beginning of track. Use the context menu in the timeScale to insert, for example: 4/4");
    document.getElementById("btn_errors").className = "btn_sequencer_blinking";
  }

  arrTimeSignatureNoBPM.forEach(function(item, idx) {
    let lengthMeasure = 0;
    let countSubBeats = 0;

    if(arrTimeSignatureNoBPM[idx+1]){
      lengthMeasure = arrTimeSignatureNoBPM[idx+1].tick - item.tick;
    }else{
      lengthMeasure = maxTick - item.tick;
      let chunk = lengthMeasure%(ppqn*4);
      lengthMeasure = lengthMeasure - chunk + ppqn*8;
    }
    //Draw timeScale measures.
    ctxScale.textAlign = "left";
    ctxScale.fillStyle = "black";
    ctxScale.font = "bold 12px system";
    ctxScale.fillText(measure+1, Math.round(item.tick/12)*scale, 9);
    item.measure = measure;

    // build array measures.
    let txt = item.timeSignature[0] +"/"+ Math.pow(2, item.timeSignature[1]);
    arrMeasures.push({tick:item.tick, measure:measure ,duration: txt});

    if(190 < ppqn || scale > 1){
      let txtBPM = synth.getCurrBPM(measure);
      ctxScale.font = "normal 11px arial";
      ctxScale.fillText(" bpm:"+txtBPM, Math.round(item.tick/12)*scale + ctxScale.measureText(measure+1).width , 8);
    }

    if (2 == item.timeSignature[1]) lengthBeat = ppqn;
    if (3 == item.timeSignature[1]) lengthBeat = ppqn/2;
    if (4 == item.timeSignature[1]) lengthBeat = ppqn/4;
    if (5 == item.timeSignature[1]) lengthBeat = ppqn/8;

    countSubBeats = Math.floor(lengthMeasure/lengthBeat);

    let countSubMeasures = Math.floor(countSubBeats/item.timeSignature[0]);
    let offset = item.tick;

    drawTimeScaleBeats(offset, lengthMeasure, lengthBeat);
    for(let i=1; i<countSubMeasures; i++){
      offset = offset + lengthMeasure/countSubMeasures;
      ctxScale.font = "bold 12px system";
      ctxScale.beginPath();
      ctxScale.moveTo(Math.round(offset/12)*scale, 15); //big measures
      ctxScale.lineTo(Math.round(offset/12)*scale, 10); //big measures
      ctxScale.stroke();
      ctxScale.fillText(item.measure+1+i, Math.round(offset/12)*scale, 9);

      arrMeasures.push({tick:offset, measure:measure+i});
    }
    measure = measure + countSubMeasures;
  });

  synth.setScaleMeasures(arrMeasures);

  //Draw flags for measures.
  arrTimeSignatureNoBPM.forEach(function(item, idx) {
    let txt = item.timeSignature[0] +"/"+ Math.pow(2, item.timeSignature[1]);
    let txtWidth = ctxScale.measureText(txt).width;

    ctxScale.beginPath();
    ctxScale.moveTo(Math.round(item.tick/12)*scale, 15); //big measures
    ctxScale.lineTo(Math.round(item.tick/12)*scale, 10); //big measures
    ctxScale.stroke();

    ctxScale.fillStyle = "white";
    ctxScale.beginPath();
    ctxScale.moveTo((Math.round(item.tick/12)*scale)+2,10);
    ctxScale.lineTo((Math.round(item.tick/12)*scale)+txtWidth+5,10);
    ctxScale.lineTo((Math.round(item.tick/12)*scale)+txtWidth,19);
    ctxScale.lineTo((Math.round(item.tick/12)*scale)+2,19);
    ctxScale.lineTo((Math.round(item.tick/12)*scale)+2,10);
    ctxScale.fill();

    ctxScale.fillStyle = "black";
    ctxScale.font = "normal 11px arial";
    ctxScale.fillText(txt, ((Math.round(item.tick/12)*scale))+2, 19);
  });
  //Draw timeScale beats.
  function drawTimeScaleBeats(offset, lengthMeasure, lengthBeat){
    ctxScale.lineWidth = 0.5;
    ctxBody.lineWidth  = 0.5;
    for(let i=0; i<lengthMeasure; i+= lengthBeat) {
      ctxScale.fillStyle = "black";
      ctxScale.beginPath();
      ctxScale.moveTo(Math.round((offset+i)/12)*scale, 20); //little measures
      ctxScale.lineTo(Math.round((offset+i)/12)*scale, 15); //little measures
      ctxScale.stroke();

      ctxBody.fillStyle = "#C3C3C3";
      ctxBody.beginPath();
      ctxBody.moveTo(Math.round((offset+i)/12)*scale, 0);
      ctxBody.lineTo(Math.round((offset+i)/12)*scale, cnvBody.height);
      ctxBody.stroke();
    }
  }
}
/**
 * [drawVertKbd description]
 * @return {[type]} [description]
 */
function drawVertKbd(){
  cnvRange = document.getElementById("canvas_rangeNotes_part2");
  ctxRange = cnvRange.getContext("2d");
  ctxRange.font = "normal 10px arial";

  arrRevers = [...synth.arrNotes].reverse();

  let y1 = 0;
  arrRevers.forEach(function(element, index) {
    if(-1 == element.indexOf("#")){
      ctxRange.strokeStyle = "black";
      ctxRange.fillStyle   = "white";

      if(-1 != element.indexOf("D") || -1 != element.indexOf("G") || -1 != element.indexOf("A")){
        ctxRange.strokeRect(2, y1, 40, 20);
        ctxRange.fillRect(2, y1, 40, 20);

        y1 = y1 + 20;
      }else{
        ctxRange.strokeRect(2, y1, 40, 15);
        ctxRange.fillRect(2, y1, 40, 15);

        y1 = y1 + 15;
      }
    }
    if (-1 != element.indexOf("C") && -1 == element.indexOf("#")) {
      ctxRange.strokeStyle = "white";
      ctxRange.fillStyle   = "black";
      ctxRange.fillText(element, 25, y1-1);
    }
  });

  y1 = 0;
  arrRevers.forEach(function(element, index) {
    if(-1 == element.indexOf("#")){
      y1 = y1 + 15;
    }else{
      ctxRange.strokeStyle = "white";
      ctxRange.fillStyle   = "black";

      if(-1 != element.indexOf("F") || -1 != element.indexOf("C#") || -1 != element.indexOf("G#")){
        y1 = y1;
      }else{
        y1 = y1 - 5;
      }

      ctxRange.strokeRect(3, y1, 25, 10);
      ctxRange.fillRect(3, y1, 25, 10);

      y1 = y1 + 5;
    }
  });
}
/**
 * [drawRangeNotes description]
 * @return {[type]} [description]
 */
function drawRangeNotes(){
  svgBody  = document.getElementById("svg_body_part2");
  cnvBody  = document.getElementById("canvas_body_part2");
	cnvRange = document.getElementById("canvas_rangeNotes_part2");

  //Calculate heght cnvRange
  let cnvRange_height = 0;
  synth.arrNotes.forEach(function(element, index) {
    if(-1 == element.indexOf("#")){
      cnvRange_height += 17;
    }
  });
  cnvRange_height = cnvRange_height;

  cnvRange.height = cnvRange_height;
  cnvRange.style.height = cnvRange_height + "px";

  cnvBody.height = cnvRange_height;
  cnvBody.style.height = cnvRange_height + "px";

  svgBody.setAttribute("height", cnvRange_height);
  svgBody.setAttribute("style","position:absolute;top:20px;background:transparent;");

  drawVertKbd();
}
/**
 * [cnvScaleMenu description]
 * e.offsetX+42 - координата на timeScale - px.
 *
 * @param  {[type]} e [description]
 */
function cnvScaleMenu(e){
  var selectedNotes;
  var ppqn = synth.getPPQN();
  var cnvScaleCoords = getCoords(cnvScale);
  var menu = document.createElement("div");
  var container = document.getElementById("win_part2");
  var oldMenuLeft = document.getElementById("menuScaleTime_left");
  var oldMenuRight = document.getElementById("menuScaleTime_right");

  if(oldMenuLeft) container.removeChild(oldMenuLeft);
  if(oldMenuRight) container.removeChild(oldMenuRight);

  let deltaOffset = 450-parseInt(cnvScaleCoords.left);
  let posX = e.clientX-cnvScaleCoords.left-deltaOffset;

  if(posX <= 560){
    menu.style.left = parseInt(posX+52) +"px";
    menu.id = "menuScaleTime_left";
  }else{
    menu.style.left = parseInt(posX-118) +"px";
    menu.id = "menuScaleTime_right";
  }
  menu.style.top = 25 +"px";
  //---------------------------------------------item1Menu
  let tickTimeSignatue;
  let item1Menu = document.createElement("label");
  item1Menu.id = "item1MenuScaleTime";
  item1Menu.innerHTML = "Add timeSignature";
  item1Menu.setAttribute("style","display:inline-block;height:20px;");

  let item1MenuInput1 = document.createElement("input");
  item1MenuInput1.type = "number";item1MenuInput1.min = "1";item1MenuInput1.max = "16";
  item1MenuInput1.value = "4";
  item1MenuInput1.id = "item1MenuInput1";
  item1MenuInput1.setAttribute("style","width:30px;padding:0;height: 18px;");
  let item1MenuSelect1 = document.createElement("select");
  item1MenuSelect1.add(new Option("4", "2"));
  item1MenuSelect1.add(new Option("8", "3"));
  item1MenuSelect1.add(new Option("16", "4"));
  item1MenuSelect1.add(new Option("32", "5"));

  item1MenuSelect1.id = "item1MenuSelect1";
  item1MenuSelect1.setAttribute("style","width:35px;padding:0;height: 18px;");
  let div_item1Menu = document.createElement("div");
  div_item1Menu.id = "div_item1Menu";
  div_item1Menu.setAttribute("style","display:flex;justify-content:space-between;align-items: center;height:20px;font-size:12px;pointer-events:all;opacity: 1.0;");
  div_item1Menu.appendChild(item1Menu);div_item1Menu.appendChild(item1MenuInput1);div_item1Menu.appendChild(item1MenuSelect1);
  menu.appendChild(div_item1Menu);

  item1Menu.addEventListener("click",function(e) {
    changedDataParams = {
      scrollTop: subwin_part2.scrollTop,
      scrollLeft: subwin_part2.scrollLeft,
      selTrackId: selTrackNum
    };
    //action...
    let offset = (parseInt(posX)+deltaOffset)*12;
    let offsetLeft = offset - offset%(ppqn/2);
    let offsetRight = offsetLeft + ppqn/2;

    offset = (offset-offsetLeft <= offsetRight-offset) ?offsetLeft :offsetRight;

    let prm1 = item1MenuInput1.value;
    let prm2 = item1MenuSelect1.value;

    let arrSignature = [prm1,prm2,24,8];
    let obj = {type: 255, subtype: 88, tick: offset, timeSignature: arrSignature};
    let endArray = true;
    let i;
    for(i=0; i<arrTimeSignature.length; i++){
      if(offset < arrTimeSignature[i].tick && "" !== item1MenuInput1.value && "" !== item1MenuSelect1.value){
        arrTimeSignature.splice(i, 0, obj);
        endArray = false;
        break;
      }
    }
    for(i+=1; i<arrTimeSignature.length; i++){
      if(88 == arrTimeSignature[i].subtype){
        arrTimeSignature[i].measure +=1;
      }
    }
    if(true == endArray)arrTimeSignature.push(obj);
    //перезаписать файл
    if("" !== item1MenuInput1.value && "" !== item1MenuSelect1.value){
      let data = saveTempMidi();
      synth.loadMIDIUrl(data);
      changedData = false;
    }

    container.removeChild(menu);
  });
  //---------------------------------------------item2Menu
  let item2Menu = document.createElement("label");
  item2Menu.id = "item2MenuScaleTime";
  item2Menu.innerHTML = "Del timeSignature";

  for(let i=0; i<arrTimeSignature.length; i++){
    if(255 == arrTimeSignature[i].type && 88 == arrTimeSignature[i].subtype){
      tickTimeSignatue = arrTimeSignature[i].tick;
      if(posX+deltaOffset >= (tickTimeSignatue-ppqn/4)/12 && posX+deltaOffset <= (tickTimeSignatue+ppqn/4)/12){
        item2Menu.innerHTML = "Del timeSignature: (tick " +tickTimeSignatue+")";
        if(arrTimeSignature[i].timeSignature){
          div_item1Menu.setAttribute("style","display:flex;justify-content:space-between;align-items: center;height:20px;font-size:12px;pointer-events:none;opacity: 0.5;");
          item2Menu.setAttribute("style","display:flex;justify-content:space-between;align-items: center;height:20px;font-size:12px;pointer-events:all;opacity: 1.0;white-space: nowrap;");
          break;
        }
      }else{
        item2Menu.setAttribute("style","display:flex;justify-content:space-between;align-items: center;height:20px;font-size:12px;pointer-events:none;opacity: 0.5;white-space: nowrap;");
      }
    }
  }
  menu.appendChild(item2Menu);

  item2Menu.addEventListener("click",function(e) {
    changedDataParams = {
      scrollTop: subwin_part2.scrollTop,
      scrollLeft: subwin_part2.scrollLeft,
      selTrackId: selTrackNum
    };
    //action...
    let i;
    let delTimeSignature = false;
    let posTimeSignature;
    for(i=0; i<arrTimeSignature.length; i++){
      posTimeSignature = arrTimeSignature[i].tick;
      if(posX+deltaOffset >= (posTimeSignature-ppqn/4)/12 && posX+deltaOffset <= (posTimeSignature+ppqn/4)/12){
        arrTimeSignature.splice(i, 1);
        delTimeSignature = true;
        break;
      }
    }
    for(i+=1; i<arrTimeSignature.length; i++){
      if(88 == arrTimeSignature[i].subtype){
        arrTimeSignature[i].measure -=1;
      }
    }

    if(true == delTimeSignature){
      //перезаписать файл
      let data = saveTempMidi();
      synth.loadMIDIUrl(data);
      changedData = false;
    }
    container.removeChild(menu);
  });
  //---------------------------------------------item3Menu
  let item3Menu = document.createElement("label");
  item3Menu.id = "item3MenuScaleTime";
  item3Menu.innerHTML = "Edit timeSignature";
  item3Menu.setAttribute("style","display:inline-block;height:20px;");
  let item3MenuInput1 = document.createElement("input");
  item3MenuInput1.type = "number";item3MenuInput1.min = "1";item3MenuInput1.max = "16";
  item3MenuInput1.value = "4";
  item3MenuInput1.id = "item3MenuInput1";
  item3MenuInput1.setAttribute("style","width:30px;padding:0;height: 18px;");
  let item3MenuSelect1 = document.createElement("select");
  item3MenuSelect1.add(new Option("4", "2"));
  item3MenuSelect1.add(new Option("8", "3"));
  item3MenuSelect1.add(new Option("16", "4"));
  item3MenuSelect1.add(new Option("32", "5"));

  item3MenuSelect1.id = "item3MenuSelect1";
  item3MenuSelect1.setAttribute("style","width:35px;padding:0;height: 18px;");
  let div_item3Menu = document.createElement("div");
  div_item3Menu.id = "div_item3Menu";
  div_item3Menu.setAttribute("style","display:flex;justify-content:space-between;align-items: center;height:20px;font-size:12px;pointer-events:all;opacity: 1.0;");
  div_item3Menu.appendChild(item3Menu);div_item3Menu.appendChild(item3MenuInput1);div_item3Menu.appendChild(item3MenuSelect1);

  menu.appendChild(div_item3Menu);

  item3Menu.addEventListener("click",function(e) {
    changedDataParams = {
      scrollTop: subwin_part2.scrollTop,
      scrollLeft: subwin_part2.scrollLeft,
      selTrackId: selTrackNum
    };
    //action...
    let offset = (parseInt(posX)+deltaOffset)*12;
    let offsetLeft = offset - offset%(ppqn/2);
    let offsetRight = offsetLeft + ppqn/2;

    offset = (offset-offsetLeft <= offsetRight-offset) ?offsetLeft :offsetRight;

    let prm1 = item3MenuInput1.value;
    let prm2 = item3MenuSelect1.value;

    let arrSignature = [prm1,prm2,24,8];
    let obj = {type: 255, subtype: 88, tick: offset, timeSignature: arrSignature};
    let endArray = true;
    let i;
    for(i=0; i<arrTimeSignature.length; i++){
      if(offset < arrTimeSignature[i].tick && "" !== item3MenuInput1.value && "" !== item3MenuSelect1.value){
        arrTimeSignature.splice(i, 0, obj);
        endArray = false;
        break;
      }
    }
    for(i+=1; i<arrTimeSignature.length; i++){
      if(88 == arrTimeSignature[i].subtype){
        arrTimeSignature[i].measure +=1;
      }
    }

    if(true == endArray)arrTimeSignature.push(obj);
    //перезаписать файл
    if("" !== item3MenuInput1.value && "" !== item3MenuSelect1.value){
      let data = saveTempMidi();
      synth.loadMIDIUrl(data);
      changedData = false;
    }

    container.removeChild(menu);
  });
  //-----------------------------
  for(let i=0; i<arrTimeSignature.length; i++){
    if(255 == arrTimeSignature[i].type && 88 == arrTimeSignature[i].subtype){
      tickTimeSignatue = arrTimeSignature[i].tick;
      if(posX+deltaOffset >= (tickTimeSignatue-ppqn/4)/12 && posX+deltaOffset <= (tickTimeSignatue+ppqn/4)/12){
        item3MenuInput1.value = arrTimeSignature[i].timeSignature[0];
        item3MenuSelect1.value = arrTimeSignature[i].timeSignature[1];
        break;
      }
    }
  }
  for(let i=0; i<arrTimeSignature.length; i++){
    if(255 == arrTimeSignature[i].type && 88 == arrTimeSignature[i].subtype){
      tickTimeSignatue = arrTimeSignature[i].tick;
      if(posX+deltaOffset >= (tickTimeSignatue-ppqn/4)/12 && posX+deltaOffset <= (tickTimeSignatue+ppqn/4)/12){
        if(arrTimeSignature[i].timeSignature){
          div_item1Menu.setAttribute("style","display:flex;justify-content:space-between;align-items: center;height:20px;font-size:12px;pointer-events:none;opacity: 0.5;");
          div_item3Menu.setAttribute("style","display:flex;justify-content:space-between;align-items: center;height:20px;font-size:12px;pointer-events:all;opacity: 1.0;");
          break;
        }
      }
    }else{
      div_item3Menu.setAttribute("style","display:flex;justify-content:space-between;align-items: center;height:20px;font-size:12px;pointer-events:none;opacity: 0.5;");
    }
  }
  //---------------------------------------------item4Menu
  let tickMeasure;
  let item4Menu = document.createElement("label");
  item4Menu.id = "item4MenuScaleTime";
  item4Menu.innerHTML = "Add measure";
  item4Menu.addEventListener("click",function(e) {
    changedDataParams = {
      scrollTop: subwin_part2.scrollTop,
      scrollLeft: subwin_part2.scrollLeft,
      selTrackId: ""
    };
    //action...
    let posMeasure;
    for(let i=0; i<arrMeasures.length; i++){
      posMeasure = arrMeasures[i].tick;
      if(posX+deltaOffset >= (posMeasure-ppqn/4)/12 && posX+deltaOffset <= (posMeasure+ppqn/4)/12){
        posMeasure = posMeasure/12;
        break;
      }
    }
    if("" !== selTrackNum){
      //Вставляем пустой такт для выбранного трека
      selectedNotes = svgBody.querySelectorAll(`[fill='#0073AA']`);
      selectedNotes.forEach(function(item, index){
        if("rect" == item.tagName){
          if( posMeasure <= parseInt(item.attributes.x.value)){
            item.attributes.x.value = parseInt(item.attributes.x.value)+(ppqn*4)/12;
          }
        }
        if("circle" == item.tagName){
          if(posMeasure <= parseInt(item.attributes.cx.value)){
            item.attributes.cx.value = parseInt(item.attributes.cx.value)+(ppqn*4)/12;
          }
        }
      });
      //перезаписать файл
      var data = saveTempMidi();
      synth.loadMIDIUrl(data);
      changedData = false;
    }else{
      //Вставляем пустой такт для всех треков
      let notes = svgBody.childNodes;
      Array.from(notes).forEach(function(item, index){
        if("rect" == item.tagName){
          if(posMeasure <= parseInt(item.attributes.x.value)){
            item.attributes.x.value = parseInt(item.attributes.x.value)+(ppqn*4)/12;
          }
        }
        if("circle" == item.tagName){
          if(posMeasure <= parseInt(item.attributes.cx.value)){
            item.attributes.cx.value = parseInt(item.attributes.cx.value)+(ppqn*4)/12;
          }
        }
      });
      //перезаписать файл
      var data = saveTempMidi();
      synth.loadMIDIUrl(data);
      changedData = false;
    }
    container.removeChild(menu);
  });
  menu.appendChild(item4Menu);
  //---------------------------------------------item5Menu
  let item5Menu = document.createElement("label");
  item5Menu.id = "item5MenuScaleTime";
  item5Menu.innerHTML = "Del measure";
  item5Menu.addEventListener("click",function(e) {
    changedDataParams = {
      scrollTop: subwin_part2.scrollTop,
      scrollLeft: subwin_part2.scrollLeft,
      selTrackId: selTrackNum
    };
    //action...
    let posMeasure;
    for(let i=0; i<arrMeasures.length; i++){
      posMeasure = arrMeasures[i].tick;
      if(posX+deltaOffset >= (posMeasure-ppqn/4)/12 && posX+deltaOffset <= (posMeasure+ppqn/4)/12){
        posMeasure = posMeasure/12;
        break;
      }
    }
    if("" !== selTrackNum){
      //Удаляем ноты для выбранного такта и выбранного трека
      selectedNotes = svgBody.querySelectorAll(`[fill='#0073AA']`);
      selectedNotes.forEach(function(item, index){
        if("rect" == item.tagName){
          if(posMeasure <= parseInt(item.attributes.x.value) &&
            posMeasure+(ppqn*4)/12 > parseInt(item.attributes.x.value)){
            item.remove();
          }
        }
        if("circle" == item.tagName){
          if(posMeasure <= parseInt(item.attributes.cx.value) &&
            posMeasure+(ppqn*4)/12 > parseInt(item.attributes.cx.value)){
            item.remove();
          }
        }
      });
      //Правую часть всех нот, выбранного трека сдвигаем влево на один такт
      selectedNotes = svgBody.querySelectorAll(`[fill='#0073AA']`);
      selectedNotes.forEach(function(item, index){
        if("rect" == item.tagName){
          if(posMeasure <= parseInt(item.attributes.x.value)){
            item.attributes.x.value = parseInt(item.attributes.x.value)-(ppqn*4)/12;
          }
        }
        if("circle" == item.tagName){
          if(posMeasure <= parseInt(item.attributes.cx.value)){
            item.attributes.cx.value = parseInt(item.attributes.cx.value)-(ppqn*4)/12;
          }
        }
      });
      //перезаписать файл
      var data = saveTempMidi();
      synth.loadMIDIUrl(data);
      changedData = false;
    }else{
      //Удаляем ноты для выбранного такта и для всех треков
      let notes = svgBody.childNodes;
      Array.from(notes).forEach(function(item, index){
        if("rect" == item.tagName){
          if(posMeasure <= parseInt(item.attributes.x.value) &&
            posMeasure+(ppqn*4)/12 > parseInt(item.attributes.x.value)){
            item.remove();
          }
        }
        if("circle" == item.tagName){
          if(posMeasure <= parseInt(item.attributes.cx.value) &&
            posMeasure+(ppqn*4)/12 > parseInt(item.attributes.cx.value)){
            item.remove();
          }
        }
      });
      //Правую часть всех нот, всех треков сдвигаем влево на один такт
      notes = svgBody.childNodes;
      Array.from(notes).forEach(function(item, index){
        if("rect" == item.tagName){
          if(posMeasure <= parseInt(item.attributes.x.value)){
            item.attributes.x.value = parseInt(item.attributes.x.value)-(ppqn*4)/12;
          }
        }
        if("circle" == item.tagName){
          if(posMeasure <= parseInt(item.attributes.cx.value)){
            item.attributes.cx.value = parseInt(item.attributes.cx.value)-(ppqn*4)/12;
          }
        }
      });
      //перезаписать файл
      var data = saveTempMidi();
      synth.loadMIDIUrl(data);
      changedData = false;
    }
    container.removeChild(menu);
  });
  menu.appendChild(item5Menu);
  //---------------------------------------------item6Menu
  let tickBPM;
  let item6Menu = document.createElement("label");
  item6Menu.id = "item6MenuScaleTime";
  item6Menu.for = "item6MenuInput";
  item6Menu.innerHTML = "Add BPM";
  item6Menu.setAttribute("style","display:inline-block;height:20px;width:115px;");

  let item6MenuInput = document.createElement("input");
  item6MenuInput.type = "number";item6MenuInput.min = "60";item6MenuInput.max = "160";
  item6MenuInput.id = "item6MenuInput";
  item6MenuInput.value = "60";
  item6MenuInput.setAttribute("style","width:40px;padding:0;height: 18px;");
  let div_item6Menu = document.createElement("div");
  div_item6Menu.id = "div_item6Menu";
  div_item6Menu.setAttribute("style","display:flex;justify-content:space-between;align-items: center;height:20px;font-size:12px;");
  div_item6Menu.appendChild(item6Menu);div_item6Menu.appendChild(item6MenuInput);
  menu.appendChild(div_item6Menu);

  item6Menu.addEventListener("click",function(e) {
    changedDataParams = {
      scrollTop: subwin_part2.scrollTop,
      scrollLeft: subwin_part2.scrollLeft,
      selTrackId: selTrackNum
    };
    //action...
    let offset = (parseInt(posX)+deltaOffset)*12;
    let offsetLeft = offset - offset%(ppqn/2);
    let offsetRight = offsetLeft + ppqn/2;

    offset = (offset-offsetLeft <= offsetRight-offset) ?offsetLeft :offsetRight;

    let obj = {type: 255, subtype: 81, tick: offset, tempoBPM: parseInt(item6MenuInput.value)};
    let endArray = true;
    for(let i=0; i<arrTimeSignature.length; i++){
      if(offset < arrTimeSignature[i].tick && "" !== item6MenuInput.value){
        arrTimeSignature.splice(i, 0, obj);
        endArray = false;
        break;
      }
    }
    if(true == endArray)arrTimeSignature.push(obj);
    //перезаписать файл
    if("" !== item6MenuInput.value){
      let data = saveTempMidi();
      synth.loadMIDIUrl(data);
      changedData = false;
    }

    container.removeChild(menu);
  });
  //---------------------------------------------item7Menu
  let item7Menu = document.createElement("label");
  item7Menu.id = "item7MenuScaleTime";
  item7Menu.innerHTML = "Del BPM";

  let i;
  for(i=0; i<arrTimeSignature.length; i++){
    if(255 == arrTimeSignature[i].type && 81 == arrTimeSignature[i].subtype){
      tickBPM = arrTimeSignature[i].tick;
      if(posX+deltaOffset >= (tickBPM-ppqn/4)/12 && posX+deltaOffset <= (tickBPM+ppqn/4)/12){
        item7Menu.innerHTML = "Del BPM: (tick " +tickBPM+")";
        if(arrTimeSignature[i].tempoBPM){
          div_item6Menu.setAttribute("style","display:flex;justify-content:space-between;align-items: center;height:20px;font-size:12px;pointer-events:none;opacity: 0.5;");
          item7Menu.setAttribute("style","display:flex;justify-content:space-between;align-items: center;height:20px;font-size:12px;pointer-events:all;opacity: 1.0;white-space: nowrap;");
          break;
        }
      }else{
        item7Menu.setAttribute("style","display:flex;justify-content:space-between;align-items: center;height:20px;font-size:12px;pointer-events:none;opacity: 0.5;white-space: nowrap;");
      }
    }
  }

  item7Menu.addEventListener("click",function(e) {
    changedDataParams = {
      scrollTop: subwin_part2.scrollTop,
      scrollLeft: subwin_part2.scrollLeft,
      selTrackId: selTrackNum
    };
    //action...
    if("" !== i){
      arrTimeSignature.splice(i, 1);
      //перезаписать файл
      let data = saveTempMidi();
      synth.loadMIDIUrl(data);
      changedData = false;
    }
    container.removeChild(menu);
  });

  menu.appendChild(item7Menu);
  //---------------------------------------------item8Menu
  let item8Menu = document.createElement("label");
  item8Menu.id = "item8MenuScaleTime";
  item8Menu.for = "item8MenuInput";
  item8Menu.innerHTML = "Edit BPM";
  item8Menu.setAttribute("style","display:inline-block;height:20px;width:115px;white-space:nowrap;");

  let item8MenuInput = document.createElement("input");
  item8MenuInput.type = "number";item8MenuInput.min = "60";item8MenuInput.max = "160";
  item8MenuInput.id = "item8MenuInput";
  item8MenuInput.value = "60";
  item8MenuInput.setAttribute("style","width:40px;padding:0;height: 18px;");
  let div_item8Menu = document.createElement("div");
  div_item8Menu.id = "div_item8Menu";
  div_item8Menu.setAttribute("style","display:flex;justify-content:space-between;align-items: center;height:20px;font-size:12px;pointer-events:all;opacity: 1.0;");
  div_item8Menu.appendChild(item8Menu);div_item8Menu.appendChild(item8MenuInput);
  menu.appendChild(div_item8Menu);
  //-----------------------------
  i = "";
  for(i=0; i<arrTimeSignature.length; i++){
    if(255 == arrTimeSignature[i].type && 81 == arrTimeSignature[i].subtype){
      tickBPM = arrTimeSignature[i].tick;
      if(posX+deltaOffset >= (tickBPM-ppqn/4)/12 && posX+deltaOffset <= (tickBPM+ppqn/4)/12){
        if(arrTimeSignature[i].tempoBPM){
          div_item6Menu.setAttribute("style","display:flex;justify-content:space-between;align-items: center;height:20px;font-size:12px;pointer-events:none;opacity: 0.5;");
          div_item8Menu.setAttribute("style","display:flex;justify-content:space-between;align-items: center;height:20px;font-size:12px;pointer-events:all;opacity: 1.0;");
          item8MenuInput.value = arrTimeSignature[i].tempoBPM;

          break;
        }
      }
    }else{
      div_item8Menu.setAttribute("style","display:flex;justify-content:space-between;align-items: center;height:20px;font-size:12px;pointer-events:none;opacity: 0.5;");
    }
  }

  item8Menu.addEventListener("click",function(e) {
    changedDataParams = {
      scrollTop: subwin_part2.scrollTop,
      scrollLeft: subwin_part2.scrollLeft,
      selTrackId: selTrackNum
    };
    //action...
    if("" !== item8MenuInput.value){
      arrTimeSignature[i].tempoBPM = parseInt(item8MenuInput.value);
      //перезаписать файл
      let data = saveTempMidi();
      synth.loadMIDIUrl(data);
      changedData = false;
    }
    container.removeChild(menu);
  });

  container.appendChild(menu);
}
function cnvScaleClick(){
  var container = document.getElementById("win_part2");
  var oldMenuLeft = document.getElementById("menuScaleTime_left");
  var oldMenuRight = document.getElementById("menuScaleTime_right");

  if(oldMenuLeft) container.removeChild(oldMenuLeft);
  if(oldMenuRight) container.removeChild(oldMenuRight);
}
/**
 * Draw notes Strip.
 */
function drawNotesStrip(){
  var ppqn  = synth.getPPQN();
  var maxTick = synth.getSongDurationTicks();

  cnvScale = document.getElementById("canvas_scale_part2");
  cnvScale.width = (Math.round((maxTick+ppqn)/12))*scale; //длинна в тиках / 12тиков на 1px
  cnvScale.style.width = cnvScale.width + "px";

  ctxScale = cnvScale.getContext("2d");
  ctxScale.clearRect(0, 0, cnvScale.width, cnvScale.height);
  cnvScale.addEventListener("contextmenu", (e) => {e.preventDefault()});

  cnvBody.width = cnvScale.width;
  cnvBody.style.width = cnvBody.width + "px";

  svgBody.setAttribute("width", cnvScale.width);
  svgBody.style.width = cnvScale.width + "px";

  ctxBody = cnvBody.getContext("2d");
  ctxBody.clearRect(0, 0, cnvBody.width, cnvBody.height);

  let y1 = 0;
  arrRevers.forEach(function(element, index){
    if(-1 == element.indexOf("#")){
      ctxBody.strokeStyle = "black";
      ctxBody.fillStyle   = "#F1F1F1";

      if(-1 != element.indexOf("D") || -1 != element.indexOf("G") || -1 != element.indexOf("A")){
        ctxBody.strokeRect(2, y1, cnvBody.width-5, 15);
        ctxBody.fillRect(2, y1, cnvBody.width-5, 15);

        if(-1 != element.indexOf("D")){
        	arrNotesTrack[index] = y1 + 5;
      	}
      	if(-1 != element.indexOf("G")){
        	arrNotesTrack[index] = y1 + 5;
      	}
      	if(-1 != element.indexOf("A")){
        	arrNotesTrack[index] = y1 + 5;
      	}

        y1 = y1 + 20;
      }else{
        ctxBody.strokeRect(2, y1, cnvBody.width-5, 15);
        ctxBody.fillRect(2, y1, cnvBody.width-5, 15);

        if(-1 != element.indexOf("C")){
        	arrNotesTrack[index] = y1 + 5;
      	}
        if(-1 != element.indexOf("E")){
        	arrNotesTrack[index] = y1;
        }
        if(-1 != element.indexOf("F")){
        	arrNotesTrack[index] = y1 + 5;
        }
        if(-1 != element.indexOf("B")){
        	arrNotesTrack[index] = y1;
        }

        y1 = y1 + 15;
      }
    }
  });
  //бемоли - диезы
  y1 = 0;
  arrRevers.forEach(function(element, index){
    if(-1 == element.indexOf("#")){
      y1 = y1 + 15;
    }else{
      if(-1 != element.indexOf("F") || -1 != element.indexOf("C#") || -1 != element.indexOf("G#")){
        y1 = y1;
      }else{
        y1 = y1 - 5;
      }
      ctxBody.strokeStyle = "black";
      ctxBody.fillStyle   = "#E3E3E3";

      ctxBody.strokeRect(2, y1, cnvBody.width-5, 8);
      ctxBody.fillRect(2, y1, cnvBody.width-5, 8);

      if(-1 != element.indexOf("C")){
      	arrNotesTrack[index] = y1;
      }
      if(-1 != element.indexOf("D")){
      	arrNotesTrack[index] = y1;
      }
      if(-1 != element.indexOf("F")){
      	arrNotesTrack[index] = y1;
      }
      if(-1 != element.indexOf("G")){
      	arrNotesTrack[index] = y1;
      }
      if(-1 != element.indexOf("A")){
      	arrNotesTrack[index] = y1;
      }

      y1 = y1 + 5;
    }
  });
  // testNotesStrip();
}

/**
 * For debug arrNotesTrack.
 * @return {[type]} [description]
 */
function testNotesStrip(){
  ctxBody.font = "normal 10px arial";
  ctxBody.strokeStyle = "white";

  arrNotesTrack.forEach(function(element, index){
  	if (element) {
  		ctxBody.fillStyle   = "#374CB6";
  		ctxBody.strokeRect(1, element, 30, 9);
	    ctxBody.fillRect(1, element, 30, 9);

	    ctxBody.fillStyle   = "white";
	    ctxBody.fillText(arrRevers[index], 3, element + 9);
  	}
  });
}
/**
 *Documentation - https://vexflow.github.io/vexflow-examples/guides/getting-started/
 *
 *Формирование листа A4 музыкальной тетради
 *отступ слева будет примерно 236 пикселей
 *отступы сверху, снизу и справа — примерно 59 пикселей
 *Рабочая зона формата A4 в пикселях обычно составляет 2480 x 3508 пикселей
 */
function printStave(){
  document.getElementById("popupStave").innerHTML = "";

  let newWin = popupCenter("", "", 1000, 600);

  var { Renderer, Stave, StaveTie, Beam, Formatter, Barline } = Vex.Flow;
  var vf = new Vex.Flow.Factory({renderer: {elementId: "popupStave", width: 960, height: 200}});
  var score = vf.EasyScore();

  vf.context.scale(.75,.75);
  vf.context.font = "bold 16px arial";

  var x = 0;
  var y = 60;

  if(0 == arrMeasures.length) return;

  let prevDuration = arrMeasures[0].duration;
  let prevMeasure  = 0;
  let measuresRest = 0;

  drawClef(arrMeasures[0].duration);
  vf.draw();

  for(let i=0; i < arrMeasures.length-1; i++){
    let duration = (arrMeasures[i].duration) ?arrMeasures[i].duration :prevDuration;
    let numMeasure = arrMeasures[i].measure;

    if(prevDuration !== duration && true == getMeasureVexFlow(numMeasure)){
      drawClef(duration);
      prevDuration = duration;
    }
    measuresRest = numMeasure - prevMeasure;
    for(let j=0; j < arrVexFlow.length; j++){
      if(numMeasure == arrVexFlow[j][0]){
        if(0 !== measuresRest){
          multiMeasureRest(measuresRest, Stave);
        }
        let parseNotes = arrVexFlow[j][1].split("/");
        let durationNotes = parseNotes[1];
        let ties = parseInt(durationNotes);

        let voice;
        let system;

        let arrScoreNotes = [];
        let posNumMeasure = 0;
        let notesMeasure;
        let drawingNotes;

        if(durationNotes.includes("w") && ties){
          let curMeasure = numMeasure+1;
          for(let k=0; k<ties; k++){

            drawingNotes = arrVexFlow[j];
            notesMeasure = score.notes(arrVexFlow[j][1], {clef: clef}, drawingNotes);

            voice = score.voice(notesMeasure).setStrict(false);
            system = makeSystem(notesMeasure.length * 60);

            system.addStave({voices: [voice]});

            posNumMeasure = system.factory.stave.x;

            vf.context.fillStyle = "blue";
            vf.context.fillText(curMeasure, posNumMeasure, 90);
            vf.context.fillStyle = "black";

            // Format and justify the notes to getStaveWidth() pixels.
            new Formatter().joinVoices([voice]).format([voice], getStaveWidth());

            const beams = Beam.generateBeams(notesMeasure);
            beams.forEach((b) => {
              b.setContext(vf.context).draw();
            });

            vf.draw();

            arrScoreNotes.push(notesMeasure);
            if(k==ties-1){
              let tties = [];
              for(let i=0; i<ties-1; i++){
                let keys = notesMeasure[0].keys.length;
                for(let j=0; j<=keys-1; j++){
                  let itm_tie = new StaveTie({
                    first_note: arrScoreNotes[i][0],
                    last_note: arrScoreNotes[i+1][0],
                    first_indices: [j],
                    last_indices: [j]
                  });
                  tties.push(itm_tie);
                }
              }
              tties.forEach((t) => {
                t.setContext(vf.context).draw();
              });
            }

            vf.draw();

            for(let note of notesMeasure){
              const svg = note.getSVGElement();
              if(svg){
                // listen to whatever event you want here
                svg.addEventListener('click', (ev) => {
                  toggleDescendantColors(svg, arrMeasures[i].tick);
                }, false);
              }
            }
            curMeasure++;
            prevMeasure = numMeasure+ties;
          }
        }else{
          drawingNotes = arrVexFlow[j];
          notesMeasure = score.notes(arrVexFlow[j][1], {clef: clef}, drawingNotes);

          let voice = score.voice(notesMeasure).setStrict(false);
          let system = makeSystem(notesMeasure.length * 60);

          if(j < arrVexFlow.length-1){
            system.addStave({voices: [voice]});
          }else{
            system.addStave({voices: [voice]}).setEndBarType(Barline.type.END);
          }

          posNumMeasure = system.factory.stave.x;

          vf.context.fillStyle = "blue";
          vf.context.fillText(numMeasure+1, posNumMeasure, 90);
          vf.context.fillStyle = "black";

          // Format and justify the notes to getStaveWidth() pixels.
          new Formatter().joinVoices([voice]).format([voice], getStaveWidth());

          const beams = Beam.generateBeams(notesMeasure);
          vf.draw();
          beams.forEach((b) => {
            b.setContext(vf.context).draw();
          });

          for(let note of notesMeasure){
            const svg = note.getSVGElement();
            if(svg){
              // listen to whatever event you want here
              svg.addEventListener('click', (ev) => {
                toggleDescendantColors(svg, arrMeasures[i].tick);
              }, false);
            }
          }
          prevMeasure = numMeasure+1;
        }

        if(1140 < posNumMeasure){

          vf = new Vex.Flow.Factory({renderer: {elementId: "popupStave", width: 960, height: 200}});
          score = vf.EasyScore();

          vf.context.scale(.75,.75);
          vf.context.font = "bold 16px arial";

          var x = 0;
          var y = 60;

          drawClef(duration);

          if(0 !== measuresRest){
            multiMeasureRest(measuresRest, Stave);
          }

          let parseNotes = arrVexFlow[j][1].split("/");
          let durationNotes = parseNotes[1];
          let ties = parseInt(durationNotes);

          let voice;
          let system;

          let arrScoreNotes = [];
          let posNumMeasure = 0;
          let notesMeasure;
          let drawingNotes;

          if(durationNotes.includes("w") && ties){
            let curMeasure = numMeasure+1;
            for(let k=0; k<ties; k++){

              drawingNotes = arrVexFlow[j];
              notesMeasure = score.notes(arrVexFlow[j][1], {clef: clef}, drawingNotes);

              voice = score.voice(notesMeasure).setStrict(false);
              system = makeSystem(notesMeasure.length * 60);

              system.addStave({voices: [voice]});

              posNumMeasure = system.factory.stave.x;

              vf.context.fillStyle = "blue";
              vf.context.fillText(curMeasure, posNumMeasure, 90);
              vf.context.fillStyle = "black";

              // Format and justify the notes to getStaveWidth() pixels.
              new Formatter().joinVoices([voice]).format([voice], getStaveWidth());

              const beams = Beam.generateBeams(notesMeasure);
              beams.forEach((b) => {
                b.setContext(vf.context).draw();
              });

              vf.draw();

              arrScoreNotes.push(notesMeasure);
              if(k==ties-1){
                let tties = [];
                for(let i=0; i<ties-1; i++){
                  let keys = notesMeasure[0].keys.length;
                  for(let j=0; j<=keys-1; j++){
                    let itm_tie = new StaveTie({
                      first_note: arrScoreNotes[i][0],
                      last_note: arrScoreNotes[i+1][0],
                      first_indices: [j],
                      last_indices: [j]
                    });
                    tties.push(itm_tie);
                  }
                }
                tties.forEach((t) => {
                  t.setContext(vf.context).draw();
                });
              }

              vf.draw();

              for(let note of notesMeasure){
                const svg = note.getSVGElement();
                if(svg){
                  // listen to whatever event you want here
                  svg.addEventListener('click', (ev) => {
                    toggleDescendantColors(svg, arrMeasures[i].tick);
                  }, false);
                }
              }
              curMeasure++;
              prevMeasure = numMeasure+ties;
            }
          }else{
            drawingNotes = arrVexFlow[j];
            notesMeasure = score.notes(arrVexFlow[j][1], {clef: clef}, drawingNotes);

            let voice = score.voice(notesMeasure).setStrict(false);
            let system = makeSystem(notesMeasure.length * 60);

            if(j < arrVexFlow.length-1){
              system.addStave({voices: [voice]});
            }else{
              system.addStave({voices: [voice]}).setEndBarType(Barline.type.END);
            }

            posNumMeasure = system.factory.stave.x;

            vf.context.fillStyle = "blue";
            vf.context.fillText(numMeasure+1, posNumMeasure, 90);
            vf.context.fillStyle = "black";

            // Format and justify the notes to getStaveWidth() pixels.
            new Formatter().joinVoices([voice]).format([voice], getStaveWidth());

            const beams = Beam.generateBeams(notesMeasure);
            vf.draw();
            beams.forEach((b) => {
              b.setContext(vf.context).draw();
            });

            for(let note of notesMeasure){
              const svg = note.getSVGElement();
              if(svg){
                // listen to whatever event you want here
                svg.addEventListener('click', (ev) => {
                  toggleDescendantColors(svg, arrMeasures[i].tick);
                }, false);
              }
            }
            prevMeasure = numMeasure+1;
          }
        }
      }
    }

    vf.draw();
  }

  let nameSong  = document.createElement("p");
  nameSong.setAttribute("style", "text-align:center;font-size:24px;width:960px;font-style: italic;");
  nameSong.innerHTML = document.getElementById("song_midi").value;

  let nameTrack = document.createElement("p");
  nameTrack.setAttribute("style", "text-align:center;font-size:24px;font-style: italic;");
  nameTrack.innerHTML = "trackName: "+document.getElementById(`part1_${selTrackNum}_trackName`).innerHTML;

  let partSong = document.createElement("p");
  partSong.setAttribute("style", "text-align:center;font-size:24px;font-style: italic;");
  partSong.innerHTML = "part: "+document.getElementById(`part1_${selTrackNum}_instr`).innerHTML;

  let divHeadList = document.createElement("div");
  divHeadList.id = "head_list";
  divHeadList.setAttribute("style", "display:flex;flex-direction: column;width:960px;");

  let divHeadSong = document.createElement("div");
  divHeadSong.id = "head_song";
  divHeadSong.setAttribute("style", "display:flex;justify-content: space-between;width:960px;");
  divHeadSong.appendChild(nameTrack); divHeadSong.appendChild(partSong);

  divHeadList.appendChild(nameSong);
  divHeadList.appendChild(divHeadSong);

  let popupStave = newWin.document.createElement("div");
  popupStave.innerHTML = document.getElementById("popupStave").innerHTML;
  popupStave.style.display = "block";
  popupStave.style.textAnchor = "middle";

  newWin.document.body.appendChild(divHeadList);
  newWin.document.body.appendChild(popupStave);

  newWin.print();

  function drawClef(duration){
    let system = makeSystem(80);
    system.addStave({
      voices: []
    }).addClef(clef).addTimeSignature(duration);
  }
  function makeSystem(width){
    var system = vf.System({ x: x, y: y, width: width});
    x += width;
    return system;
  }
  function multiMeasureRest(numRest){
    let stave = new Stave(x, y, 100);
    stave.setContext(vf.context).draw();
    let mRestLeft = new Vex.Flow.MultiMeasureRest(numRest,stave);
    mRestLeft.setStave(stave);
    mRestLeft.setContext(vf.context).draw();
    x += 100;
  }
}
function popupCenter(url, title, w, h){
  // Учтите возможность использования двух мониторов
  const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : window.screenX;
  const dualScreenTop = window.screenTop !== undefined ? window.screenTop : window.screenY;

  const width  = window.innerWidth || document.documentElement.clientWidth || screen.width;
  const height = window.innerHeight || document.documentElement.clientHeight || screen.height;

  const systemZoom = width / window.screen.availWidth;
  const left = (width - w) / 2 / systemZoom + dualScreenLeft;
  const top  = (height - h) / 2 / systemZoom + dualScreenTop;
  const newWindow = window.open(url, title, `scrollbars=yes, width=${w / systemZoom}, height=${h / systemZoom}, top=${top}, left=${left}`);

  if (window.focus) newWindow.focus();

  return newWindow;
}
function getStaveWidth(){
  let staveWidth = 0;
  let allMeausuresCount = 0;
  let allNotesCount = 0;
  let allMultiRest = 0;

  let numCurMeasure = 0;
  let allAbsences = 0;

  //Считаем количество смен размера такта
  for(let i=0; i < arrMeasures.length-1; i++){
    let numMeasure = arrMeasures[i].measure;
    if(arrMeasures[i].duration && true == getMeasureVexFlow(numMeasure)){
      allMeausuresCount++;
    }
  }

  //Считаем количество нот и количество пауз
  arrVexFlow.forEach(function(item, idx){
    let notesCount = item[1].split(",").length;
    allNotesCount += notesCount;

    if(item[0] == numCurMeasure){
      // numCurMeasure++;
    }else{
      numCurMeasure = item[0];
      allAbsences++;
    }
    numCurMeasure++;
  });

  staveWidth = allAbsences*100 + allMeausuresCount*50 + allNotesCount*40;

  return staveWidth;
}
function getMeasureVexFlow(numMeasure){
  let search = false;
  arrVexFlow.forEach(function(item, idx){
    if(item[0] == numMeasure) search = true;
  });
  return search;
}
/**
 * [btnsStave description]
 * @param  {[type]} id [description]
 * @return {[type]}    [description]
 */
function btnsStave(id){

  switch(id){
    case "treble":
      clef = "treble";
      document.getElementById("treble").style.boxShadow =
      "inset -2px -2px 3px rgba(200, 200, 200, .6),inset 2px 2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("bass").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("alto").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("tenor").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("soprano").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("percussion").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("french").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      break;
    case "bass":
      clef = "bass";
      document.getElementById("treble").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("bass").style.boxShadow =
      "inset -2px -2px 3px rgba(200, 200, 200, .6),inset 2px 2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("alto").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("tenor").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("soprano").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("percussion").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("french").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      break;
    case "alto":
      clef = "alto";
      document.getElementById("treble").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("bass").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("alto").style.boxShadow =
      "inset -2px -2px 3px rgba(200, 200, 200, .6),inset 2px 2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("tenor").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("soprano").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("percussion").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("french").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      break;
    case "tenor":
      clef = "tenor";
      document.getElementById("treble").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("bass").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("alto").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("tenor").style.boxShadow =
      "inset -2px -2px 3px rgba(200, 200, 200, .6),inset 2px 2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("soprano").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("percussion").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("french").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      break;
    case "soprano":
      clef = "soprano";
      document.getElementById("treble").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("bass").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("alto").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("tenor").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("soprano").style.boxShadow =
      "inset -2px -2px 3px rgba(200, 200, 200, .6),inset 2px 2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("percussion").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("french").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      break;
    case "percussion":
      clef = "percussion";
      document.getElementById("treble").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("bass").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("alto").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("tenor").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("soprano").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("percussion").style.boxShadow =
      "inset -2px -2px 3px rgba(200, 200, 200, .6),inset 2px 2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("french").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      break;
    case "french":
      clef = "french";
      document.getElementById("treble").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("bass").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("alto").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("tenor").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("soprano").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("percussion").style.boxShadow =
      "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)";
      document.getElementById("french").style.boxShadow =
      "inset -2px -2px 3px rgba(200, 200, 200, .6),inset 2px 2px 3px rgba(0, 0, 0, .6)";
      break;
  }
  //Для формирования arrMusicalPart с выбранным clef
  drawNotesSong();

  drawVexflow();
}
/**
 * [drawVexflow description]
 * documentation    https://vexflow.github.io/vexflow-examples/guides/getting-started/
 *
 * @return {[type]} [description]
 */
function drawVexflow(){
  document.getElementById("stave").innerHTML="";

  var { Renderer, Stave, StaveTie, Beam, Formatter, Barline } = Vex.Flow;
  var vf = new Vex.Flow.Factory({renderer: {elementId: "stave", width: getStaveWidth(), height: 155},});

  var score = vf.EasyScore();

  vf.context.scale(.6,.6);
  vf.context.font = "bold 16px arial";

  var x = 0;
  var y = 60;

  if(0 == arrMeasures.length) return;

  let prevDuration = arrMeasures[0].duration;
  let prevMeasure  = 0;
  let measuresRest = 0;

  drawClef(arrMeasures[0].duration);
  vf.draw();

  for(let i=0; i < arrMeasures.length-1; i++){
    let duration = (arrMeasures[i].duration) ?arrMeasures[i].duration :prevDuration;
    let numMeasure = arrMeasures[i].measure;
    let posNumMeasure = 0;
    let notesMeasure = [];

    if(prevDuration !== duration && true == getMeasureVexFlow(numMeasure)){
      drawClef(duration);
      prevDuration = duration;
    }
    measuresRest = numMeasure - prevMeasure;
    for(let j=0; j < arrVexFlow.length; j++){
      if(numMeasure == arrVexFlow[j][0]){
        if(0 < measuresRest){
          multiMeasureRest(measuresRest);
        }

        let parseNotes = arrVexFlow[j][1].split("/");
        let durationNotes = parseNotes[1];
        let ties = parseInt(durationNotes);

        let voice;
        let system;

        let arrScoreNotes = [];

        if(durationNotes.includes("w") && ties){
          let curMeasure = numMeasure+1;
          for(let k=0; k<ties; k++){

            let drawingNotes = arrVexFlow[j];
            notesMeasure = score.notes(arrVexFlow[j][1], {clef: clef}, drawingNotes);

            voice = score.voice(notesMeasure).setStrict(false);
            system = makeSystem(notesMeasure.length * 60);

            system.addStave({voices: [voice]});

            posNumMeasure = system.factory.stave.x;

            vf.context.fillStyle = "blue";
            vf.context.fillText(curMeasure, posNumMeasure, 90);
            vf.context.fillStyle = "black";

            // Format and justify the notes to getStaveWidth() pixels.
            new Formatter().joinVoices([voice]).format([voice], getStaveWidth());

            const beams = Beam.generateBeams(notesMeasure);
            beams.forEach((b) => {
              b.setContext(vf.context).draw();
            });

            vf.draw();

            arrScoreNotes.push(notesMeasure);
            if(k==ties-1){
              let tties = [];
              for(let i=0; i<ties-1; i++){
                let keys = notesMeasure[0].keys.length;
                for(let j=0; j<=keys-1; j++){
                  let itm_tie = new StaveTie({
                    first_note: arrScoreNotes[i][0],
                    last_note: arrScoreNotes[i+1][0],
                    first_indices: [j],
                    last_indices: [j]
                  });
                  tties.push(itm_tie);
                }
              }
              tties.forEach((t) => {
                t.setContext(vf.context).draw();
              });
            }

            vf.draw();

            for(let note of notesMeasure){
              const svg = note.getSVGElement();
              if(svg){
                // listen to whatever event you want here
                svg.addEventListener('click', (ev) => {
                  toggleDescendantColors(svg, arrMeasures[i].tick);
                }, false);
              }
            }
            curMeasure++;
            prevMeasure = numMeasure+ties;
          }
        }else{
          let drawingNotes = arrVexFlow[j];
          notesMeasure = score.notes(arrVexFlow[j][1], {clef: clef}, drawingNotes);

          let voice = score.voice(notesMeasure).setStrict(false);
          let system = makeSystem(notesMeasure.length * 60);

          if(j < arrVexFlow.length-1){
            system.addStave({voices: [voice]});
          }else{
            system.addStave({voices: [voice]}).setEndBarType(Barline.type.END);
          }

          posNumMeasure = system.factory.stave.x;

          vf.context.fillStyle = "blue";
          vf.context.fillText(numMeasure+1, posNumMeasure, 90);
          vf.context.fillStyle = "black";

          // Format and justify the notes to getStaveWidth() pixels.
          new Formatter().joinVoices([voice]).format([voice], getStaveWidth());

          const beams = Beam.generateBeams(notesMeasure);
          vf.draw();
          beams.forEach((b) => {
            b.setContext(vf.context).draw();
          });

          for(let note of notesMeasure){
            const svg = note.getSVGElement();
            if(svg){
              // listen to whatever event you want here
              svg.addEventListener('click', (ev) => {
                toggleDescendantColors(svg, arrMeasures[i].tick);
              }, false);
            }
          }
          prevMeasure = numMeasure+1;
        }
      }
    }
  }

  let svgStave = document.getElementById("stave").querySelector("svg");
  svgStave.style.textAnchor = "middle";

  //Definition of the end of the score
  let stavebarlines = document.querySelectorAll(".vf-stavebarline");
  let lastStavebarline = stavebarlines[stavebarlines.length-1];
  let item_lastStavebarline = lastStavebarline.querySelector("rect");
  let svgStaveWidth = parseInt(item_lastStavebarline.getAttribute('x'))+25;

  svgStave.setAttribute('viewBox', `0 0 ${svgStaveWidth} 225`);
  svgStave.setAttribute('width', svgStaveWidth*.6);
  svgStave.style.width = svgStaveWidth * .6 + "px";
  svgStave.style.height = "auto";

  arrMeasuresStave = [];
  let arrTxtItems = svgStave.querySelectorAll("text");
  for(let item of arrTxtItems){
    let txt  = item.innerHTML;
    let posX = item.attributes.x.value;
    arrMeasuresStave.push({measure: parseInt(txt), posX: parseInt(posX)});
  }
  synth.setStaveMeasures(arrMeasuresStave);

  // parentItem: SVGElement
  function toggleDescendantColors(parentItem, posptrTimeX){
    const isSelected = parentItem.classList.contains('selected');
    // Choose the color based on whether it’s selected or not.
    const newColor = isSelected ? 'black' : 'red';

    // Toggle the colors on all child elements.
    parentItem.querySelectorAll('*').forEach((child) => {
      child.setAttribute('fill', newColor);
      child.setAttribute('stroke', newColor);
    });

    // Update the selection state on the parent element.
    if (isSelected) {
      parentItem.classList.remove('selected');
    } else {
      parentItem.classList.add('selected');

      //Set positionX of ptrTime
      let posX = (0 == posptrTimeX) ? -2 : (posptrTimeX/12)-4;
      posX = posX * scale;
      ptrTime.style.left = posX +"px";
      subwin_part2.scrollLeft = posX-360;
    }
  }

  function drawClef(duration){
    let system = makeSystem(80);
    system.addStave({
      voices: []
    }).addClef(clef).addTimeSignature(duration);
  }
  function makeSystem(width){
    var system = vf.System({ x: x, y: y, width: width});
    x += width;
    return system;
  }
  function multiMeasureRest(numRest){
    let stave = new Stave(x, y, 100);
    stave.setContext(vf.context).draw();
    let mRestLeft = new Vex.Flow.MultiMeasureRest(numRest,stave);
    mRestLeft.setStave(stave);
    mRestLeft.setContext(vf.context).draw();
    x += 100;
  }

  //Array of notes with errors from VexFlow
  arrBadNotes = score.badNotes();
}
/**
 * [ptrTimeDown description]
 * @param  {[type]} e [description]
 * @return {[type]}   [description]
 */
function ptrTimeDown(e){
  var ptrTime = document.getElementById('ptrTime');
  ptrTimeIsDragging = true;
  ptrTimeStartX = e.clientX; // начальная координата курсора по X
  ptrTimeInitLeft = ptrTime.offsetLeft; // начальная позиция элемента по X
  ptrTime.style.cursor = 'grabbing'; // меняем курсор
}
/**
 * [ptrTimeMove description]
 * @param  {[type]} e [description]
 * @return {[type]}   [description]
 */
function ptrTimeMove(e){
  if (!ptrTimeIsDragging) return;
  e.preventDefault(); // предотвращаем выделение текста
  const deltaX = e.clientX - ptrTimeStartX; // вычисляем смещение по X
  if(ptrTimeInitLeft + deltaX < 0){
    ptrTime.style.left = 0;
    ptrTimeIsDragging =false;
  }else if(e.clientX > 1135){
    ptrTime.style.left = (ptrTimeInitLeft + deltaX) + 'px';
    ptrTimeIsDragging =false;
  }else{
    ptrTime.style.left = (ptrTimeInitLeft + deltaX) + 'px';
  }
}
/**
 * [ptrTimeUp description]
 * @param  {[type]} e [description]
 * @return {[type]}   [description]
 */
function ptrTimeUp(e){
  ptrTimeIsDragging = false;
  ptrTimeStartX = null;
  ptrTimeInitLeft = null;
  ptrTime.style.cursor = 'grab'; // возвращаем курсор
}
/**
 * [ptrTimeStaveDown description]
 * @param  {[type]} e [description]
 * @return {[type]}   [description]
 */
function ptrTimeStaveDown(e){
  var ptrTimeStave = document.getElementById('ptrTimeStave');
  ptrTimeStaveIsDragging = true;
  ptrTimeStaveStartX = e.clientX; // начальная координата курсора по X
  ptrTimeStaveInitLeft = ptrTimeStave.offsetLeft; // начальная позиция элемента по X
  ptrTimeStave.style.cursor = 'grabbing'; // меняем курсор
}
/**
 * [ptrTimeStaveMove description]
 * @param  {[type]} e [description]
 * @return {[type]}   [description]
 */
function ptrTimeStaveMove(e){
  if (!ptrTimeStaveIsDragging) return;
  e.preventDefault(); // предотвращаем выделение текста
  const deltaX = e.clientX - ptrTimeStaveStartX; // вычисляем смещение по X
  if(ptrTimeStaveInitLeft + deltaX < 0){
    ptrTimeStave.style.left = 0;
    ptrTimeStaveIsDragging =false;
  }else if(e.clientX > 1150){
    ptrTimeStave.style.left = (ptrTimeStaveInitLeft + deltaX) + 'px';
    ptrTimeStaveIsDragging =false;
  }else{
    ptrTimeStave.style.left = (ptrTimeStaveInitLeft + deltaX) + 'px';
  }
}
/**
 * [ptrTimeStaveUp description]
 * @param  {[type]} e [description]
 * @return {[type]}   [description]
 */
function ptrTimeStaveUp(e){
  ptrTimeStaveIsDragging = false;
  ptrTimeStaveStartX = null;
  ptrTimeStaveInitLeft = null;
  ptrTimeStave.style.cursor = 'grab'; // возвращаем курсор
}
/**
 * [ptrTime description]
 * @param  {[type]} e [description]
 * @return {[type]}   [description]
 */
function ptrTimeDragDrope(e){
  ptrTime.style.cursor = "grab";

  var ptrTimeCoords  = getCoords(ptrTime);
  var cnvScaleCoords = getCoords(cnvScale);
  var shiftX = e.clientX - ptrTimeCoords.left;

  var cnvScaleMin = cnvScaleCoords.left + shiftX;
  var cnvScaleMax = cnvScaleCoords.left + cnvScale.offsetWidth - shiftX;

  function moveAt(e) {
    var left = e.clientX - cnvScaleCoords.left - shiftX ;
    if(e.clientX < cnvScaleMin) left = 0;
    if(e.clientX > cnvScaleMax) left = cnvScale.offsetWidth - ptrTime.offsetWidth;
    ptrTime.style.left = left + "px";

    //Move the player slider
    synth.locateMIDI(left*12);
  }
  document.onmousemove = function(e){
    moveAt(e);
  }
  document.onmouseup = function(e){
    document.onmousemove = null;
    ptrTime.style.cursor = "pointer";
  }
}
/**
 * [drawHeaderInfo description]
 * @return {[type]} [description]
 */
function drawHeaderInfo(){
  let typeMidi = synth.getTypeMidi();

  if(0 == typeMidi){
    let arrTracksInfo = getTracksInfo();
    document.getElementById("tracks_count").value =  arrTracksInfo.length-1;
  }else if(1 == typeMidi){
    document.getElementById("tracks_count").value =  midiFile.header.getTracksCount()-1;
  }
  document.getElementById("song_midi").value = synth.getCopyright();
  document.getElementById("tempoBPM").value = synth.getTempoBPM();
  document.getElementById("midi_type").value = synth.getTypeMidi();
  document.getElementById("ppqn").value = synth.getPPQN();
}
function getTracksInfo(){
  let typeMidi = synth.getTypeMidi();
  let tracksCount = 0;
  let arrTracksInfo = [];

  if(0 === typeMidi){
    let trackEvents = getTrackEventsType0();
    let arr = [];
    for(let j=0; j<trackEvents.length; j++){
      if(trackEvents[j] && 8 !== trackEvents[j].subtype && 9 !== trackEvents[j].subtype){
        arr.push(trackEvents[j]);
      }
    }
    arrTracksInfo = arr;
  }else if(1 === typeMidi){
    tracksCount = midiFile.header.getTracksCount();
    for(let i=0; i<tracksCount; i++){
      let trackEvents = midiFile.getTrackEvents(i);
      let arr = [];
      for(let j=0; j<trackEvents.length; j++){
        if(trackEvents[j] && 8 !== trackEvents[j].subtype && 9 !== trackEvents[j].subtype){
          arr.push(trackEvents[j]);
        }
      }
      arrTracksInfo[i] = arr;
    }
  }else if(2 === typeMidi){

  }

  return arrTracksInfo;
}
/**
 * [drawTrackInfo description]
 * @return {[type]} [description]
 */
function drawTrackInfo(){
  var arrTracksInfo = getTracksInfo();

  arrTrkCh = [];

  //Tracks info.
  arrTracksInfo.forEach(function(track, index){
    let bank_num;

    if(0 == index)return;

    document.getElementById("part1_" + index).setAttribute("numtrk",index);

    let lblTrackName = document.getElementById("part1_" + index + "_trackName");
    let lblChannel = document.getElementById("part1_" + index + "_ch");
    let lblInstrument = document.getElementById("part1_" + index + "_instr");
    let lblBank = document.getElementById("part1_" + index + "_elemBank");
    let lblPreset = document.getElementById("part1_" + index + "_elemPreset");

    let trackName = "Track "+index;
    lblTrackName.innerHTML = trackName;

    track.forEach(function(item, idx){
      //Track name.
      if(3 == track[idx].subtype) {
        track[idx].data.forEach(function(val, i){
          track[idx].data[i] = String.fromCharCode(val);
        });
        trackName = track[idx].data.join("");
        if("" !== trackName) lblTrackName.innerHTML = trackName;
      }
      //Channel.
      if(11 == track[idx].subtype || 12 == track[idx].subtype) {
        let ch = track[idx].channel;
        arrTrkCh[ch] = index;
        lblChannel.innerHTML = "Channel:" + parseInt(ch+1);
      }
      //Instrument (prog)
      if(11 == track[idx].subtype && 32 == track[idx].param1){
        bank_num = track[idx].param2;
      }
      if(11 == track[idx].subtype || 12 == track[idx].subtype){
        if(9 == track[idx].channel){
          lblInstrument.innerHTML = "Standard Drum Kit";
        }
      }
      if(12 == track[idx].subtype){
        let numInstr = track[idx].param1;
        if("GM" == standMidi){
          if(9 == track[idx].channel){
            lblInstrument.innerHTML = "Standard Drum Kit";
          }else{
            lblInstrument.innerHTML = synth.getProgNameGM(0, numInstr) + " (prog:" + numInstr + ")";
          }
        }else{
          if(9 == track[idx].channel){
            lblBank.innerHTML = "";
            lblPreset.innerHTML = "";
          }else{
            //Preset (num)
            let presetNum  = track[idx].param1;
            let presetName = "";
            if(soundFont){
              bank_num = (bank_num) ?(bank_num) :curBank;
              let obj = soundFont.banks;
              //by default curBank=0
              let filteredArray = obj[bank_num].presets.filter(element => element !== null);
              if(undefined !== filteredArray){
                let idxPreset = filteredArray.findIndex(item => item.header.preset === presetNum);
                if(-1 !== idxPreset) presetName = filteredArray[idxPreset].header.name;
              }
              lblBank.innerHTML = "bank:"+bank_num;
              lblPreset.innerHTML = "preset:" + presetNum;
              lblInstrument.innerHTML = presetName;
            }
          }
        }
      }
    });
  });

  synth.setTrkCh(arrTrkCh);

  //setChannelsPlay
  arrChannelsPlay = [];
  selTrackNum = "";
  let channels = document.getElementsByClassName("lbl_ch");
  Array.from(channels).forEach(function(item, index){
    let textCh = item.textContent.split(":");
    if (textCh[1]){
      let ch = parseInt(textCh[1]-1);
      arrChannelsPlay.push(ch);
    }
  });
  synth.setChannelsPlay(arrChannelsPlay);
}
/**
 * Select Part1 Item
 * @param  {[type]} id [description]
 * @return {[type]}    [description]
 */
async function selTrack(id){
  var stave      = document.getElementById("stave");
  var items      = document.getElementById("win_part1").childNodes;
  var nameTrk    = document.getElementById("part1_" + id + "_trackName");
  var instrument = document.getElementById("part1_" + id + "_instr");
  var channel    = document.getElementById("part1_" + id + "_ch");
  var lblBank    = document.getElementById("part1_" + id + "_elemBank");
  var lblPreset  = document.getElementById("part1_" + id + "_elemPreset");

  if(click_speaker){
    click_speaker = false;
    return;
  }
  if(click_sliders){
    click_sliders = false;
    return;
  }

  if(true == changedData){
    changedDataParams = {
      scrollTop: subwin_part2.scrollTop,
      scrollLeft: subwin_part2.scrollLeft,
      selTrackId: id
    };
    var data = await saveTempMidi();
    await synth.loadMIDIUrl(data);
    changedData = false;
  }else{
    items.forEach(function(item, index){
      if ("part1_"+id == item.id) {
        if ("rgb(128, 128, 128)" == item.style.background){
          item.style.background = "#C1C1C1";
          selTrackNum = "";
          synth.setChannelSel("");
        }else{
          item.style.background = "#808080";
          let numtrk = document.getElementById("part1_"+id).attributes.numtrk;
          if(undefined !== numtrk){
            selTrackNum = parseInt(numtrk.value);
          }
        }
      }else{
        item.style.background = "#C1C1C1";
      }
    });
    drawNotesSong();
  }

  if("" == selTrackNum){
    resetAllBtns();
    document.getElementById("stave").innerHTML = "";
    document.getElementById("btn_errors").className = "btn_sequencer";
    return;
  }

  let numCh = channel.innerHTML.split(":");
  numCh = parseInt(numCh[1]);
  ch.value = numCh;
  curCh    = numCh-1;
  synth.setChannelSel(curCh);

  if(false == synth.playing){
    if(9 == curCh){
      document.getElementById("sf2viewer").style.display = "none";

      prog.value = curNote;
      ProgChange();

      var w=synth.drummap[curNote-35];
      if(undefined == w){
        document.getElementById("prog1").style.visibility = "hidden";
        document.getElementById("prog2").style.visibility = "hidden";
        document.getElementById("prog3").style.visibility = "hidden";
        document.getElementById("prog4").style.visibility = "hidden";
        document.getElementById("oscs").style.visibility = "hidden";
        document.getElementById("head_soundeditorGM").innerHTML = "Timbre Editor";
        return;
      }
      document.getElementById("soundeditorGM").style.display = "table";
      document.getElementById("soundViewerGSXG").style.display = "none";
      document.getElementById("oscs").style.visibility = "visible";

      ViewParam(w);
      EnableRow();
      audioNodesDiagram();
    }else{
      if("GM" == standMidi){
        document.getElementById("sf2viewer").style.display = "none";
        let prog_num;
        let prog_name

        prog_num = instrument.innerHTML.split(":");
        prog_num = prog_num[1].replace(/[()]/g, "");

        curProg = prog_num;
        prog.value = prog_num;
        ProgChange();

        ViewParam(w);
        EnableRow();

        document.getElementById("soundeditorGM").style.display = "table";
        document.getElementById("soundViewerGSXG").style.display = "none";
      }else{
        document.getElementById("sf2viewer").style.display = "block";

        let prog_num = lblPreset.innerHTML.split(":");
        prog_num = prog_num[1];
        document.getElementById("soundeditorGM").style.display = "none";
        document.getElementById("soundViewerGSXG").style.display = "table";

        curProg = prog_num;
        prog.value = prog_num;

        let numBank = lblBank.innerHTML.split(":");
        numBank = parseInt(numBank[1]);
        curBank = (numBank) ? numBank : 0;
        let progName = synth.getProgNameSF(curBank, curProg);
        document.getElementById("prog_num").value = curProg;
        document.getElementById("prog_name").innerHTML = progName;

        getInfoPrmSF('banks',curBank,'', 'banks');
        ProgChange();
      }
    }
    audioNodesDiagram();
    synth.send([0x90+curCh,curNote+curOct*12,100],0);
    let interval = setTimeout(() => {
      synth.send([0x80+curCh,curNote+curOct*12,100],0);
      clearTimeout(interval);
    }, 500);
  }

  document.getElementById("ptrTimeStave").style.left = "0px";

  //for cerate arrBadNotes
  drawVexflow();
  document.getElementById("stave").innerHTML="";
  if(0 === arrBadNotes.length){
    document.getElementById("btn_errors").className = "btn_sequencer";
  }else{
    document.getElementById("btn_errors").className = "btn_sequencer_blinking";
  }

  switch(pressBtn){
    case "btn_tools":
      document.getElementById("stave").className = "staveTools";
      openTools();
      if(btnTool) drawTools(btnTool);
      break;
    case "btn_help":
      document.getElementById("stave").className = "staveHelp";
      openHelp();
      break;
    case "btn_stave":
      document.getElementById("stave").className = "staveStave";
      openStave();
      break;
    case "btn_trackInfo":
      document.getElementById("stave").className = "staveTrack";
      openTrackInfo();
      break;
    case "btn_setting":
      document.getElementById("stave").className = "staveSetting";
      openSetting();
      break;
    case "btn_errors":
      document.getElementById("stave").className = "staveErrors";
      drawBadNotes();
      break;
  }
}
function sliders_vol(id){


  click_sliders = true;
}
function sliders_pan(id){


  click_sliders = true;
}
/**
 * [speaker_on_off description]
 * @param  {[type]} id [description]
 * @return {[type]}    [description]
 */
function speaker_on_off(id){
  let items1  = id.split("_");
  let lbl_txt = document.getElementById("part1_"+items1[1]+"_ch").innerHTML;
  let items2 = lbl_txt.split(":");
  let ch = parseInt(items2[1]-1);

  if ("speakerOn" == items1[0] || "speaker" == items1[0]){
    document.getElementById("speaker_" + items1[1]).style.display = "none";
    document.getElementById("speakerOn_" + items1[1]).style.display = "none";
    document.getElementById("speakerOff_" + items1[1]).style.display = "block";
    //delete element from array
    let chIndex = arrChannelsPlay.indexOf(ch);
    if (chIndex !== -1) {
      arrChannelsPlay.splice(chIndex, 1);
    }
  }else{
    document.getElementById("speakerOff_" + items1[1]).style.display = "none";
    document.getElementById("speakerOn_" + items1[1]).style.display = "none";
    document.getElementById("speaker_" + items1[1]).style.display = "block";
    //insert element to array
    if (-1 != ch) {
      arrChannelsPlay.push(ch);
    }
  }

  if(true == synth.playing){
    synth.stopMIDI();
    synth.setChannelsPlay(arrChannelsPlay);
    synth.playMIDI();
  }else{
    synth.setChannelsPlay(arrChannelsPlay);
  }

  click_speaker = true;
}
function getTrackEventsType0(){
  let bpm = synth.getTempoBPM();
  let ppqn = synth.getPPQN();

  let ch = 0;
  let arr = [];
  let trackEvents = midiFile.getEvents();

  trackEvents.forEach(function(item, index){
    if(255 == item.type &&
      2 == item.subtype || 81 == item.subtype || 84 == item.subtype || 88 == item.subtype || 89 == item.subtype){
      if(undefined == arr[0]) arr[0]=[];
      arr[0].push(item);
    }else if(8 == item.type){
      ch = item.channel;
      if(undefined == arr[ch+1]) arr[ch+1]=[];
      arr[ch+1].push(item);
    }
  });

  // arr = arr.sort((a, b) => a.channel - b.channel);
  //  && 11 !== item.subtype && 12 !== item.subtype && 14 !== item.subtype

  arr = arr.filter(Boolean);

  //Calculate of the playTick parameter
  arr.forEach(function(items, index){
    for(let i=0; i <= items.length-1; i++){
      let item = items[i];
      if(8 == item.type && 9 == item.subtype){
        item.playTick = Math.round((Math.round(item.playTime) * bpm * ppqn)/60000);
      }
      if(8 == item.type && 8 == item.subtype){
        item.playTick = Math.round((Math.round(item.playTime) * bpm * ppqn)/60000);
      }
    }
  });

  return arr;
}
/**
 * [drawNotesSong description]
 *
 * @param  {[type]} scale [description]
 */
function drawNotesSong(){
  var typeMidi    = synth.getTypeMidi();
  var ppqn        = synth.getPPQN();
  var tracksCount =  0;

  svgBody.innerHTML = "";

  //Build arrTracksEvents.
  if(false == changedData){
    arrTracksEvents = [];
    if(0 === typeMidi){
      arrTracksEvents = getTrackEventsType0();
      tracksCount = arrTracksEvents.length;
    }else if(1 === typeMidi){
      tracksCount = midiFile.header.getTracksCount();
      for(let i=0; i <= tracksCount-1; i++){
        arrTracksEvents[i] = midiFile.getTrackEvents(i);
      }
    }else if(2 === typeMidi){

    }
  }

  for(let i=0; i <= tracksCount; i++){
    let offset = 0;
    let itemsPrev = [];

    if(i == parseInt(selTrackNum))continue;
    if(undefined == arrTracksEvents[i])continue;

    arrTracksEvents[i].forEach(function(item, index){

      if(1 === typeMidi){
        offset += item.delta;
      }

      if(0 < i){
        if(undefined == arrVelocity[i]) arrVelocity[i] = [];
        if(undefined == arrVolume[i]) arrVolume[i] = [];
        if(undefined == arrPitchBend[i]) arrPitchBend[i] = [];
        if(undefined == arrPanpos[i]) arrPanpos[i] = [];
        if(undefined == arrExpression[i]) arrExpression[i] = [];
        if(undefined == arrModulation[i]) arrModulation[i] = [];
        if(undefined == arrSustain[i]) arrSustain[i] = [];
        if(undefined == arrSostenuto[i]) arrSostenuto[i] = [];
        if(undefined == arrSoftPedal[i]) arrSoftPedal[i] = [];
        if(undefined == arrBalance[i]) arrBalance[i] = [];

        //for arrTools()------------------------
        if(item && 8 == item.type && 14 == item.subtype) {
          let obj = {posX:offset, param1:item.param1, param2:item.param2};
          let found = arrPitchBend[i].some(itm=>{
            return itm.posX === offset && itm.param1 === item.param1 && itm.param2 === item.param2;
          });
          if(false == found) arrPitchBend[i].push(obj);
        }
        if(item && 8 == item.type && 11 == item.subtype && 1 == item.param1) {
          let obj = {posX:offset, value:item.param2};
          let found = arrModulation[i].some(itm=>{
            return itm.posX === offset && itm.value === item.param2
          });
          if(false == found) arrModulation[i].push(obj);
        }
        if(item && 8 == item.type && 11 == item.subtype && 7 == item.param1) {
          let obj = {posX:offset, value:item.param2};
          let found = arrVolume[i].some(itm=>{
            return itm.posX === offset && itm.value === item.param2
          });
          if(false == found) arrVolume[i].push(obj);
        }
        if(item && 8 == item.type && 11 == item.subtype && 8 == item.param1) {
          let obj = {posX:offset, value:item.param2};
          let found = arrBalance[i].some(itm=>{
            return itm.posX === offset && itm.value === item.param2
          });
          if(false == found) arrBalance[i].push(obj);
        }
        if(item && 8 == item.type && 11 == item.subtype && 10 == item.param1) {
          let obj = {posX:offset, value:item.param2};
          let found = arrPanpos[i].some(itm=>{
            return itm.posX === offset && itm.value === item.param2
          });
          if(false == found) arrPanpos[i].push(obj);
        }
        if(item && 8 == item.type && 11 == item.subtype && 11 == item.param1) {
          let obj = {posX:offset, value:item.param2};
          let found = arrExpression[i].some(itm=>{
            return itm.posX === offset && itm.value === item.param2
          });
          if(false == found) arrExpression[i].push(obj);
        }
        if(item && 8 == item.type && 11 == item.subtype && 64 == item.param1) {
          let obj = {posX:offset, value:item.param2};
          let found = arrSustain[i].some(itm=>{
            return itm.posX === offset && itm.value === item.param2
          });
          if(false == found) arrSustain[i].push(obj);
        }
        if(item && 8 == item.type && 11 == item.subtype && 66 == item.param1) {
          let obj = {posX:offset, value:item.param2};
          let found = arrSostenuto[i].some(itm=>{
            return itm.posX === offset && itm.value === item.param2
          });
          if(false == found) arrSostenuto[i].push(obj);
        }
        if(item && 8 == item.type && 11 == item.subtype && 67 == item.param1) {
          let obj = {posX:offset, value:item.param2};
          let found = arrSoftPedal[i].some(itm=>{
            return itm.posX === offset && itm.value === item.param2
          });
          if(false == found) arrSoftPedal[i].push(obj);
        }
        //-----------------------------------------
      }
      if(0 === typeMidi){
        if(item && 8 == item.type && 9 == item.subtype) {
          itemsPrev[item.param1] = item;
        }
        if(item && 8 == item.type && 8 == item.subtype) {
          let deltaOffset = item.playTick - itemsPrev[item.param1].playTick;
          let velocity = itemsPrev[item.param1].param2;

          drawNote(item.channel,item.param1,velocity,Math.round(itemsPrev[item.param1].playTick/12),Math.round(deltaOffset/12),"#444444");
        }
      }
      if(1 === typeMidi){
        if(item && 8 == item.type && 9 == item.subtype) {
          itemsPrev[item.param1] = item;
          itemsPrev[item.param1].offset = (itemsPrev[item.param1].offset) ?itemsPrev[item.param1].offset :offset;
        }
        if(item && 8 == item.type && 8 == item.subtype) {
          let prevOffset  = itemsPrev[item.param1].offset;
          let deltaOffset = offset - prevOffset;
          let velocity = itemsPrev[item.param1].param2;

          drawNote(item.channel,item.param1,velocity,Math.round(prevOffset/12),Math.round(deltaOffset/12),"#444444");
        }
      }
    });
  }

  if("" !== selTrackNum) {
    let offset = 0;
    let itemsPrev = [];
    let acordOffset = 0;

    arrMusicalPart = [];
    arrVexFlow = [];

    let keys = [];
    if(undefined == arrTracksEvents[selTrackNum]) return;

    arrTracksEvents[selTrackNum].forEach(function(item, index){

      if(1 === typeMidi){
        offset += item.delta;
      }

      if(0 === typeMidi){
        if(item && 8 == item.type && 9 == item.subtype) {
          itemsPrev[item.param1] = item;
        }
        if(item && 8 == item.type && 8 == item.subtype) {
          let deltaOffset = item.playTick - itemsPrev[item.param1].playTick;
          let velocity = itemsPrev[item.param1].param2;

          if(acordOffset !== item.playTick){
            if(0 !== keys.length) arrMusicalPart.push({ keys: keys });
            keys = [];
          }
          keys.push(synth.arrNotes[item.param1-21]+":"+deltaOffset+":"+item.playTick);
          acordOffset = item.playTick;

          drawNote(item.channel,item.param1,velocity,Math.round(itemsPrev[item.param1].playTick/12),Math.round(deltaOffset/12),"#0073AA");
        }
      }
      if(1 === typeMidi){
        if(8 == item.type && 9 == item.subtype) {
          itemsPrev[item.param1] = item;
          itemsPrev[item.param1].offset = (itemsPrev[item.param1].offset) ?itemsPrev[item.param1].offset :offset;
        }
        if(8 == item.type && 8 == item.subtype) {
          let prevOffset  = itemsPrev[item.param1].offset;
          let deltaOffset = offset - prevOffset;
          let velocity = itemsPrev[item.param1].param2;

          if(acordOffset !== prevOffset){
            if(0 !== keys.length){
              arrMusicalPart.push({ keys: keys});
              keys = [];
            }
          }

          keys.push(synth.arrNotes[item.param1-21]+":"+deltaOffset+":"+prevOffset);

          acordOffset = prevOffset;

          drawNote(item.channel,item.param1,velocity,Math.round(prevOffset/12),Math.round(deltaOffset/12),"#0073AA");
        }
      }
    });

    if(0 !== keys.length) {
      arrMusicalPart.push({keys: keys});
      keys = [];
    }

    //Преобразование массива arrMusicalPart под формат VexFlow.
    arrMusicalPart.forEach(function(item, index){
      let prm1 = [];  //notes name
      let prm2 = 0;   //notes duration
      let prm3 = 0;   //notes tick On
      let prm4 = 0;   //notes width
      Array.from(item.keys).forEach(function(itm, idx){
        let arr = itm.split(":");
        prm1.push(arr[0]);
        prm2 = durationNote(parseInt(arr[1]));
        prm3 = parseInt(arr[2]);
        prm4 = parseInt(arr[1]);
      });
      arrMusicalPart[index] = {keys:prm1, duration:prm2, tick:prm3, delta:prm4};
    });

    if(0 !== arrMusicalPart.length) arrVexFlowNotes();
  }
}
function arrVexFlowNotes(){
  let ppqn = synth.getPPQN();
  let minMusicalPartTick = parseInt(arrMusicalPart[0].tick);
  let maxMusicalPartTick = parseInt(arrMusicalPart.at(-1).tick) + parseInt(arrMusicalPart.at(-1).delta);

  let str = "";
  arrMusicalPart.forEach(function(item, index){
    if(1 < item.keys.length){
      str = "(" + item.keys.toString().replaceAll(",", " ") + ")";
    }else{
      str = item.keys.toString();
    }
    item.keys = str;
  });

  for(let i=0; i < arrMeasures.length; i++){
    let chunk = "";
    let notes = [];
    let numMeasure = arrMeasures[i].measure;
    let maxLength = synth.getSongDurationTicks();
    let firstNoteMeasure = false;
    let lastNoteMeasure = {};

    let measureTick = parseInt(arrMeasures[i].tick);
    let measureNextTick = (arrMeasures[i+1]) ? parseInt(arrMeasures[i+1].tick) : maxLength;

    for(let j=0; j < arrMusicalPart.length; j++){
      let noteTick = arrMusicalPart[j].tick;
      let noteDelta = (arrMusicalPart[j].delta > ppqn*4) ? ppqn*4 : arrMusicalPart[j].delta;

      if(measureTick <= arrMusicalPart[j].tick && measureNextTick > arrMusicalPart[j].tick){
        if(false == firstNoteMeasure){
          let deltaTick = arrMusicalPart[j].tick - arrMeasures[i].tick;
          let rest = getRest(deltaTick);
          if(rest) notes.push(rest);
          firstNoteMeasure = true;
          if(noteTick >= measureTick && measureNextTick && noteTick + noteDelta <= measureNextTick){
            notes.push(arrMusicalPart[j].keys + "/" + arrMusicalPart[j].duration);
            lastNoteMeasure = arrMusicalPart[j];
          }
          if(arrMusicalPart[j+1]){
            let deltaTick = arrMusicalPart[j+1].tick - (arrMusicalPart[j].tick + arrMusicalPart[j].delta);
            let rest = getRest(deltaTick);
            if(rest) notes.push(rest);
          }
        }else{
          if(noteTick >= measureTick && measureNextTick && noteTick + noteDelta <= measureNextTick){
            notes.push(arrMusicalPart[j].keys + "/" + arrMusicalPart[j].duration);
            lastNoteMeasure = arrMusicalPart[j];
          }
          if(arrMusicalPart[j+1] && measureNextTick > arrMusicalPart[j+1].tick){
            let deltaTick = arrMusicalPart[j+1].tick - (arrMusicalPart[j].tick + arrMusicalPart[j].delta);
            let rest = getRest(deltaTick);
            if(rest) notes.push(rest);
          }
        }
      }
    }
    if({} !== lastNoteMeasure){
      let deltaTick = measureNextTick - (lastNoteMeasure.tick + lastNoteMeasure.delta);
      let rest = getRest(deltaTick);
      if(rest) notes.push(rest);
      lastNoteMeasure = {};
    }
    if(0 !== notes.length) arrVexFlow.push([numMeasure, notes.toString()]);
  }

  function getRest(deltaTick){
    let ppqn = synth.getPPQN();
    let rest;

    switch(deltaTick){
      case Math.trunc(ppqn*2):  //  1/2
        if("treble" == clef)rest = "B4/2/r";
        if("bass" == clef)rest = "D3/2/r";
        if("alto" == clef)rest = "C4/2/r";
        if("tenor" == clef)rest = "A3/2/r";
        if("soprano" == clef)rest = "G4/2/r";
        if("percussion" == clef)rest = "B4/2/r";
        if("french" == clef)rest = "B4/2/r";
        break;
      case ppqn:                //  1/4
        if("treble" == clef)rest = "B4/4/r";
        if("bass" == clef)rest = "D3/4/r";
        if("alto" == clef)rest = "C4/4/r";
        if("tenor" == clef)rest = "A3/4/r";
        if("soprano" == clef)rest = "G4/4/r";
        if("percussion" == clef)rest = "B4/4/r";
        if("french" == clef)rest = "B4/4/r";
        break;
      case  Math.trunc(ppqn/2): //  1/8
        if("treble" == clef)rest = "B4/8/r";
        if("bass" == clef)rest = "D3/8/r";
        if("alto" == clef)rest = "C4/8/r";
        if("tenor" == clef)rest = "A3/8/r";
        if("soprano" == clef)rest = "G4/8/r";
        if("percussion" == clef)rest = "B4/8/r";
        if("french" == clef)rest = "B4/8/r";
        break;
      case  Math.trunc(ppqn/4): //  1/16
        if("treble" == clef)rest = "B4/16/r";
        if("bass" == clef)rest = "D3/16/r";
        if("alto" == clef)rest = "C4/16/r";
        if("tenor" == clef)rest = "A3/16/r";
        if("soprano" == clef)rest = "G4/16/r";
        if("percussion" == clef)rest = "B4/16/r";
        if("french" == clef)rest = "B4/16/r";
        break;
      case  Math.trunc(ppqn/8): //  1/32
        if("treble" == clef)rest = "B4/32/r";
        if("bass" == clef)rest = "D3/32/r";
        if("alto" == clef)rest = "C4/32/r";
        if("tenor" == clef)rest = "A3/32/r";
        if("soprano" == clef)rest = "G4/32/r";
        if("percussion" == clef)rest = "B4/32/r";
        if("french" == clef)rest = "B4/32/r";
        break;
    }
    return rest;
  }
}
/**
 * Draw Note rect.
 *
 * @param  {[type]} ch     [description]
 * @param  {[type]} note   [description]
 * @param  {[type]} offset [description]
 * @param  {[type]} delta  [description]
 * @param  {[type]} color  [description]
 */
function drawNote(ch, note, velocity, offset, delta, color, pos_Y){
  let ppqn = synth.getPPQN();
  let valTransformRect = "scale("+scale+", 1)";
  let valTransformCircle = "translate("+offset*(scale-1)+"px)";
  let posY;
  if("" !== note){
    let noteNum = Math.abs(note-108);
    posY = arrNotesTrack[noteNum];
  }else{
    posY = pos_Y;
  }

  let numTrk;
  let part1_items = document.getElementById("win_part1").childNodes;
  for(let i=1; i<=part1_items.length; i++){
    let val = document.getElementById("part1_"+i+"_ch").innerHTML.split(":");
    let item = parseInt(val[1].trim());
    if (item && item-1 == ch){
      numTrk = i;
      break;
    }
  }

  if (9 == ch){
    let circle = document.createElementNS(svgns, "circle");
    circle.setAttribute("trk", numTrk);
    circle.setAttribute("ch", ch);
    circle.setAttribute("note", note);
    circle.setAttribute("velocity", velocity);
    circle.setAttribute("r", 4);
    circle.setAttribute("cx", offset);
    circle.setAttribute("cy", posY+ 4);
    circle.setAttribute("fill", color);
    circle.style.transform = valTransformCircle;

    circle.addEventListener("contextmenu", function(e) {
      changedData = true;
      circle.remove();
    });
    circle.addEventListener("mousedown",function(e){
      circle.style.opacity = "0.4";

      let circleCoords  = getCoords(circle);
      let svgBodyCoords = getCoords(svgBody);
      let shiftX = (e.clientX - circleCoords.left-circleCoords.width);
      let svgBodyMin = (svgBodyCoords.left + shiftX);
      let svgBodyMax = (svgBodyCoords.left + svgBodyCoords.width - circleCoords.width + shiftX);

      function moveAt(e){
        let left = (e.clientX - svgBodyCoords.left - shiftX)/scale;
        if(e.clientX < svgBodyMin) left = 0;
        if(e.clientX > svgBodyMax) left = svgBodyCoords.width - circleCoords.width;
        circle.attributes.cx.value = left;
        circle.setAttribute("fill", "green");
        changedData = true;
      }
      document.onmousemove = function(e){
        moveAt(e);
        svgBody.setAttribute("action", "note_move");
      }
      document.onmouseup = function(e){
        document.onmousemove = null;
        document.onmouseup = null;
        svgBody.setAttribute("action", "");
      }
    });
    circle.addEventListener("mouseup",function(e){
      circle.style.opacity = "1";

      let chunk = 0;
      //update leftSide position
      chunk = circle.attributes.cx.value%(Math.round(ppqn/4/12));
      circle.attributes.cx.value = circle.attributes.cx.value - chunk;
    });

    circle.style.cursor = "move";

    svgBody.appendChild(circle);
  }else{
    let action = "";
    let rect = document.createElementNS(svgns, "rect");
    rect.setAttribute("trk", numTrk);
    rect.setAttribute("ch", ch);
    rect.setAttribute("note", note);
    rect.setAttribute("velocity", velocity);
    rect.setAttribute("x", offset);
    rect.setAttribute("y", posY);
    rect.setAttribute("width", delta);
    rect.setAttribute("height", 8);
    rect.setAttribute("fill", color);
    rect.setAttribute("stroke", "black");
    rect.setAttribute("stroke-width", "1");
    rect.style.transform = valTransformRect;

    rect.addEventListener("contextmenu", function(e) {
      changedData = true;
      rect.remove();
    });
    rect.addEventListener("mousedown", function(e){
      action = rect.style.cursor;
      rect.style.opacity = "0.4";

      let rectCoords  = getCoords(rect);
      let svgBodyCoords = getCoords(svgBody);
      let shiftX = (e.clientX - rectCoords.left);
      let svgBodyMin = (svgBodyCoords.left + shiftX);
      let svgBodyMax = (svgBodyCoords.left + svgBodyCoords.width - rectCoords.width + shiftX);

      function resizeAt(e){
        let posRectLeft = rectCoords.left - svgBodyCoords.left;
        let width = (e.clientX - svgBodyCoords.left - shiftX - posRectLeft + rectCoords.width)/scale;
        rect.attributes.width.value = (width > ppqn/4/12) ?width :ppqn/4/12;
        rect.setAttribute("fill", "red");
        svgBody.setAttribute("action", "note_resize");
        changedData = true;
      }
      function moveAt(e){
        let left = (e.clientX - svgBodyCoords.left - shiftX)/scale;
        if(e.clientX < svgBodyMin) left = 0;
        if(e.clientX > svgBodyMax) left = svgBodyCoords.width - rectCoords.width;
        rect.attributes.x.value = left;
        rect.setAttribute("fill", "green");
        svgBody.setAttribute("action", "note_move");
        changedData = true;
      }
      document.onmousemove = function(e){
        if("move" == action) {
          moveAt(e);
          svgBody.setAttribute("action", "note_move");
        }
        if("ew-resize" == action) {
          resizeAt(e);
          svgBody.setAttribute("action", "note_resize");
        }
      }
      document.onmouseup = function(e){
        document.onmousemove = null;
        document.onmouseup = null;
        svgBody.setAttribute("action", "");
      }
    });
    rect.addEventListener("mouseup",function(e){
      let chunk = 0;
      action = "";
      rect.style.opacity = "1";
      //update leftSide position
      chunk = rect.attributes.x.value%(Math.round(ppqn/4/12));
      rect.attributes.x.value = rect.attributes.x.value - chunk;
      //update rect width
      let rectWidth = rect.attributes.width.value;
      let rectWidthMin = Math.round(ppqn/4/12);
      if(rectWidth < rectWidthMin)rectWidth = rectWidthMin;
      chunk = rectWidth%(rectWidthMin);
      rect.attributes.width.value = rect.attributes.width.value - chunk;
    });
    rect.addEventListener("mousemove",function(e){
      let mouseX = e.offsetX;

      let posX = parseInt(rect.attributes.x.value)*scale;
      let noteWidth = parseInt(rect.attributes.width.value)*scale;

      if (posX < mouseX && mouseX < posX + noteWidth-2){
        rect.style.cursor = "move";
      }
      if (posX+noteWidth-2 < mouseX && mouseX < posX+noteWidth+2){
        rect.style.cursor = "ew-resize";
      }
    });

    svgBody.appendChild(rect);
  }
}
/**
 * InsertNote - use right mouse key
 * Insert selNoteStorage note from localStorage - use Alt key + right mouse key
 *
 * @return {[type]} [description]
 */
function insertNote(e){
  let ppqn  = synth.getPPQN();
  let svgBodyCoords = getCoords(svgBody);
  let noteNum;

  let posX = parseInt((e.clientX - svgBodyCoords.left)/scale);
  let chunkX = posX%(Math.round(ppqn/4/12));
  posX -= chunkX;

  let posY = parseInt(e.clientY - svgBodyCoords.top);
  for(let i=0; i < arrNotesTrack.length; i++){
    if( arrNotesTrack[i] < posY && posY < arrNotesTrack[i+1]){
      posY = arrNotesTrack[i];
      break;
    }
  }

  if("" === curCh || "" === selTrackNum)return;

  if(true == e.altKey){
    //Insert selNoteStorage note from localStorage
    let selNoteStorage = JSON.parse(localStorage.getItem("selNoteStorage"));

    let parser = new DOMParser();
    selNoteStorage.forEach(function(item, index){
      let itemDOM = parser.parseFromString(item, "image/svg+xml");
      let element = itemDOM.documentElement;

      selNoteStorage[index] = element;
    });

    let numTrk;
    let part1_items = document.getElementById("win_part1").childNodes;
    for(let i=1; i<=part1_items.length; i++){
      let val = document.getElementById("part1_"+i+"_ch").innerHTML.split(":");
      let item = parseInt(val[1].trim());
      if (item && item-1 == curCh){
        numTrk = i;
        break;
      }
    }

    //Definition of minX minY reference point
    let minX =("rect" == selNoteStorage[0].tagName) ?selNoteStorage[0].getAttribute("x") :selNoteStorage[0].getAttribute("cx");
    minX = parseInt(minX);
    let minY =("rect" == selNoteStorage[0].tagName) ?selNoteStorage[0].getAttribute("y") :selNoteStorage[0].getAttribute("cy");
    minY = parseInt(minY);

    //Definition of maxX value "x"/"cx" + "width/2r"
    let maxX =("rect" == selNoteStorage[0].tagName) ?selNoteStorage[0].getAttribute("x") :selNoteStorage[0].getAttribute("cx");
    maxX = parseInt(maxX);
    //Definition of maxY value "y"/"cy" + "width/2r"
    let maxY =("rect" == selNoteStorage[0].tagName) ?selNoteStorage[0].getAttribute("y") :selNoteStorage[0].getAttribute("cy");
    maxY = parseInt(maxY);

    selNoteStorage.forEach(function(item, idx){
      let curX = ("rect" == item.tagName) ?item.getAttribute("x") :item.getAttribute("cx");
      let curY = ("rect" == item.tagName) ?item.getAttribute("y") :item.getAttribute("cy");

      if(parseInt(curX) < minX) minX = parseInt(curX);
      if(parseInt(curY) < minY) minY = parseInt(curY);

      let itemWidth = ("rect" == item.tagName) ?item.getAttribute("width") :12;
      if(parseInt(curX) + parseInt(itemWidth) > maxX) maxX = parseInt(curX) + parseInt(itemWidth);
      if(parseInt(curY) > maxY) maxY = parseInt(curY);
    });
    let deltaX = posX - minX;  //posX - position of cursor //minX - min value "x"/"cx" //deltaX - aprox coord to cursor posX
    let deltaY = posY - minY;  //posY - position of cursor //minY - min value "y"/"cy" //deltaY - aprox coord to cursor posY
    let valX;
    let valY;
    //Bringing notes coordinates to a reference point
    selNoteStorage.forEach(function(item, idx){
      if(("rect" == item.tagName)){
        valX = parseInt(item.getAttribute("x"));
        valX += deltaX;
        item.setAttribute("x", valX);
        valY = parseInt(item.getAttribute("y"));
        valY += deltaY;
        item.setAttribute("y", valY);
      }
      if(("circle" == item.tagName)){
        valX = parseInt(item.getAttribute("cx"));
        valX += deltaX;
        item.setAttribute("cx", valX);
        valY = parseInt(item.getAttribute("cy"));
        valY += deltaY;
        item.setAttribute("cy", valY);
      }
      //note number determination
      for(let i=0; i < arrNotesTrack.length; i++){
        if( arrNotesTrack[i] <= valY && valY < arrNotesTrack[i+1]){
          noteNum = arrNotesTrack.length-i;
          noteNum = noteNum + 20;
          break;
        }
      }

      let velocity = item.getAttribute("velocity");
      let offset   = (9 == curCh) ?item.getAttribute("cx") : item.getAttribute("x");
      let width    = (9 == curCh) ?item.getAttribute("r") : item.getAttribute("width");

      if("" !== curCh && noteNum && offset && width) drawNote(curCh, noteNum, velocity, offset, width, "blue");
    });

    if(svgBody.querySelector('#groupBorder')) svgBody.removeChild(groupBorder);

    groupBorder = document.createElementNS(svgns,'rect');
    groupBorder.id = "groupBorder";

    groupBorder.setAttribute('x', posX);
    groupBorder.setAttribute('y', posY);
    groupBorder.setAttribute('width', maxX-minX);
    groupBorder.setAttribute('height', maxY-minY+10);

    groupBorder.addEventListener("mousedown", groupBorderMouseDown);
    groupBorder.addEventListener("mousemove",groupBorderMouseMove);
    groupBorder.addEventListener("mouseup", groupBorderMouseUp);

    svgBody.appendChild(groupBorder);

    selectNotes(posX,posY,maxX-minX,maxY-minY+10);

    changedData = true;
    return;
  }
  //------------------------------------------------------------------------
  let delta = Math.round(ppqn/2/12);
  let color = "brown";

  for(let i=0; i < arrNotesTrack.length; i++){
    if( arrNotesTrack[i] <= posY && posY < arrNotesTrack[i+1]){
      posY = arrNotesTrack[i];
      noteNum = arrNotesTrack.length-i;
      noteNum = noteNum + 20;
      break;
    }
  }
  //update posX position
  let chunk = posX%(Math.round(ppqn/4/12));
  let velocity = "60";
  posX = posX - chunk;

  changedData = true;
  if(undefined !== noteNum) drawNote(curCh, noteNum, velocity, posX, delta, color, posY);

}
function getCoords(element){
  let box = element.getBoundingClientRect();
  return {
    left: box.left,
    top: box.top,
    width: box.width,
    height: box.height,
  }
}
/**
 * [durationNote description]
 * @param  {integer} lengthNote [description]
 * @return {array}   duration   [description]
 */
function durationNote(lengthNote){
  let ppqn = synth.getPPQN();
  let durationNote;
  let deltaL = 0;
  let deltaR = 0;

  //подгонка lengthNote к длине бита.
  if(ppqn*4 <= lengthNote){
    let ties = Math.round(lengthNote / (ppqn*4));
    durationNote = (1 == ties) ?"w" : ties+"w";
  }else if(ppqn*2 <= lengthNote && ppqn*4 > lengthNote){
    // 1/2 -h
    deltaL = lengthNote - ppqn*2;
    deltaR = ppqn*4 - lengthNote;
    if(0 == deltaL){
      durationNote = "2";
    }else if(ppqn == deltaL){
      durationNote = "2.";
    }else if(deltaL < deltaR){
      durationNote = "2";
    }else{
      durationNote = "w";
    }
  }else if(ppqn <= lengthNote && ppqn*2 > lengthNote){
    // 1/4 -q
    deltaL = lengthNote - ppqn;
    deltaR = ppqn*2 - lengthNote;
    if(0 == deltaL){
      durationNote = "4";
    }else if(ppqn/2 == deltaL){
      durationNote = "4.";
    }else if(deltaL < deltaR){
      durationNote = "4";
    }else{
      durationNote = "2";
    }
  } else if(ppqn/2 <= lengthNote && ppqn > lengthNote){
    // 1/8 -8
    deltaL = lengthNote - ppqn/2;
    deltaR = ppqn - lengthNote;
    if(0 == deltaL){
      durationNote = "8";
    }else if(ppqn/4 == deltaL){
      durationNote = "8.";
    }else if(deltaL < deltaR){
      durationNote = "8";
    }else{
      durationNote = "4";
    }
  }else if(ppqn/4 <= lengthNote && ppqn/2 > lengthNote){
    // 1/16 -16
    deltaL = lengthNote - ppqn/4;
    deltaR = ppqn/2 - lengthNote;
    if(0 == deltaL){
      durationNote = "16";
    }else if(ppqn/8 == deltaL){
      durationNote = "16.";
    }else if(deltaL < deltaR){
      durationNote = "16";
    }else{
      durationNote = "8";
    }
  }else if(ppqn/8 <= lengthNote && ppqn/4 > lengthNote){
    // 1/32 -32
    deltaL = lengthNote - ppqn/8;
    deltaR = ppqn/4 - lengthNote;
    if(0 == deltaL){
      durationNote = "32";
    }else if(ppqn/16 == deltaL){
      durationNote = "32.";
    }else if(deltaL < deltaR){
      durationNote = "32";
    }else{
      durationNote = "16";
    }
  }else if(ppqn/8 > lengthNote) durationNote = "32";

  if(undefined == durationNote) console.log("Invalid length Note:",lengthNote);

  return durationNote;
}
/**
 * [btnScale description]
 * @param  {[type]} id [description]
 * @return {[type]}    [description]
 */
function btnScale(id){
  let div_scale = document.getElementById(id);

  if("scale+" == id){scale=scale*2;if(scale > 4) scale = 4;}
  if("scale0" == id){scale=1;}
  if("scale-" == id){scale=scale/2;if(scale < .25) scale = .25;}

  div_scale.style.background="white";
  setTimeout(() => {
    div_scale.style.background="#C3C3C3";
  }, 250);
  div_scale.style.background="white";

  synth.setScale(scale);

  ptrTime.style.left = "-2px";

  drawNotesStrip();
  drawTimeScale();
  drawNotesSong();
  if("btn_tools" == pressBtn)drawTools(btnTool);
}
/**
 * [viewHelp description]
 */
function viewHelp(){
  let div  = document.getElementById("stave");
  let tblHelp = document.createElement("table");
  tblHelp.id = "tbl_help";

  let row = document.createElement("tr");
  let td_1 = document.createElement("td");
  td_1.setAttribute("colspan", 2);
  td_1.setAttribute("style", "text-align: center;background: #E3E3E3");
  td_1.innerHTML = "<b>Button assignment</b>";
  row.appendChild(td_1);
  tblHelp.appendChild(row);

  let row0 = document.createElement("tr");
  let td0_1 = document.createElement("th");
  td0_1.innerHTML = "Button";
  let td0_2 = document.createElement("th");
  td0_2.innerHTML = "Action";
  row0.appendChild(td0_1); row0.appendChild(td0_2);
  tblHelp.appendChild(row0);

  let row1 = document.createElement("tr");
  let td1_1 = document.createElement("td");
  td1_1.innerHTML = "Save";
  let td1_2 = document.createElement("td");
  td1_2.innerHTML = "Saving data in midi format (type 1)";
  row1.appendChild(td1_1); row1.appendChild(td1_2);
  tblHelp.appendChild(row1);

  let row2 = document.createElement("tr");
  let td2_1 = document.createElement("td");
  td2_1.innerHTML = "New";
  let td2_2 = document.createElement("td");
  td2_2.innerHTML = "Creating midi data (type 1)";
  row2.appendChild(td2_1); row2.appendChild(td2_2);
  tblHelp.appendChild(row2);

  let row3 = document.createElement("tr");
  let td3_1 = document.createElement("td");
  td3_1.innerHTML = "Export";
  let td3_2 = document.createElement("td");
  td3_2.innerHTML = "Export MIDI file to selected audio format";
  row3.appendChild(td3_1); row3.appendChild(td3_2);
  tblHelp.appendChild(row3);

  let row4 = document.createElement("tr");
  let td4_1 = document.createElement("td");
  td4_1.innerHTML = "+Track";
  let td4_2 = document.createElement("td");
  td4_2.innerHTML = "Add empty track before selected one track";
  row4.appendChild(td4_1); row4.appendChild(td4_2);
  tblHelp.appendChild(row4);

  let row5 = document.createElement("tr");
  let td5_1 = document.createElement("td");
  td5_1.innerHTML = "-Track";
  let td5_2 = document.createElement("td");
  td5_2.innerHTML = "Delete selected track";
  row5.appendChild(td5_1); row5.appendChild(td5_2);
  tblHelp.appendChild(row5);

  let row6 = document.createElement("tr");
  let td6_1 = document.createElement("td");
  td6_1.innerHTML = "Report";
  let td6_2 = document.createElement("td");
  td6_2.innerHTML = "Content of midi file (offset -- field name : value)";
  row6.appendChild(td6_1); row6.appendChild(td6_2);
  tblHelp.appendChild(row6);

  let row7 = document.createElement("tr");
  let td7_1 = document.createElement("td");
  td7_1.innerHTML = "Record";
  let td7_2 = document.createElement("td");
  td7_2.innerHTML = "Recording a new track";
  row7.appendChild(td7_1); row7.appendChild(td7_2);
  tblHelp.appendChild(row7);

  let row8 = document.createElement("tr");
  let td8_1 = document.createElement("td");
  td8_1.innerHTML = "Verify";
  let td8_2 = document.createElement("td");
  td8_2.innerHTML = "1.sets the notes of the entire song to a PPQN/4 grid.<br>2.сhecking for duplicate notes";
  row8.appendChild(td8_1); row8.appendChild(td8_2);
  tblHelp.appendChild(row8);

  let row9 = document.createElement("tr");
  let td9_1 = document.createElement("td");
  td9_1.innerHTML = "Track";
  let td9_2 = document.createElement("td");
  td9_2.innerHTML = "get/set information for adjustment on the selected track";
  row9.appendChild(td9_1); row9.appendChild(td9_2);
  tblHelp.appendChild(row9);

  let row10 = document.createElement("tr");
  let td10_1 = document.createElement("td");
  td10_1.innerHTML = "Stave";
  let td10_2 = document.createElement("td");
  td10_2.innerHTML = "getting the musical notation of the selected track";
  row10.appendChild(td10_1); row10.appendChild(td10_2);
  tblHelp.appendChild(row10);

  let row11 = document.createElement("tr");
  let td11_1 = document.createElement("td");
  td11_1.innerHTML = "Tools";
  let td11_2 = document.createElement("td");
  td11_2.innerHTML = "midi editing tools (see: Tools)<br>left mouse click & move - draw graph<br>right mouse click - clear graph";
  row11.appendChild(td11_1); row11.appendChild(td11_2);
  tblHelp.appendChild(row11);

  let row12 = document.createElement("tr");
  let td12_1 = document.createElement("td");
  td12_1.innerHTML = "Setting";
  let td12_2 = document.createElement("td");
  td12_2.innerHTML = "Save a list of SoundFonts for use by the synthesizer.<br>Saving API key for converting midi to audio format.";
  row12.appendChild(td12_1); row12.appendChild(td12_2);
  tblHelp.appendChild(row12);

  let row13 = document.createElement("tr");
  let td13_1 = document.createElement("td");
  td13_1.innerHTML = "Examples";
  let td13_2 = document.createElement("td");
  td13_2.innerHTML = "Examples of using MIDI file editing tools.";
  row13.appendChild(td13_1); row13.appendChild(td13_2);
  tblHelp.appendChild(row13);


  let row47 = document.createElement("tr");
  let td47_1 = document.createElement("td");
  td47_1.innerHTML = "Errors";
  let td47_2 = document.createElement("td");
  td47_2.innerHTML = "Errors in a MIDI file where the total duration of notes in a measure is not equal to the time signature of the current measure.";
  row47.appendChild(td47_1); row47.appendChild(td47_2);
  tblHelp.appendChild(row47);

  let row48 = document.createElement("tr");
  let td48_1 = document.createElement("td");
  td48_1.innerHTML = "Rules";
  let td48_2 = document.createElement("td");
  td48_2.innerHTML = "Rules for editing MIDI files.";
  row48.appendChild(td48_1); row48.appendChild(td48_2);
  tblHelp.appendChild(row48);


  let row14 = document.createElement("tr");
  let td14_1 = document.createElement("td");
  td14_1.setAttribute("colspan", 2);
  td14_1.setAttribute("style", "text-align: center;background: #E3E3E3");
  td14_1.innerHTML = "<b>Edit notes on selected track (using mouse buttons)</b>";
  row14.appendChild(td14_1);
  tblHelp.appendChild(row14);

  let row15 = document.createElement("tr");
  let td15_1 = document.createElement("td");
  td15_1.innerHTML = "<span style='color: brown'>insert</span> note";
  let td15_2 = document.createElement("td");
  td15_2.innerHTML = "Select track and right click the area of notes";
  row15.appendChild(td15_1); row15.appendChild(td15_2);
  tblHelp.appendChild(row15);

  let row16 = document.createElement("tr");
  let td16_1 = document.createElement("td");
  td16_1.innerHTML = "<span style='color: black'>delete</span> note";
  let td16_2 = document.createElement("td");
  td16_2.innerHTML = "Deselect track and right click on the note.";
  row16.appendChild(td16_1); row16.appendChild(td16_2);
  tblHelp.appendChild(row16);

  let row17 = document.createElement("tr");
  let td17_1 = document.createElement("td");
  td17_1.innerHTML = "<span style='color: green'>move</span> note";
  let td17_2 = document.createElement("td");
  td17_2.innerHTML = "Press left button mouse and move left or right";
  row17.appendChild(td17_1); row17.appendChild(td17_2);
  tblHelp.appendChild(row17);

  let row18 = document.createElement("tr");
  let td18_1 = document.createElement("td");
  td18_1.innerHTML = "<span style='color: red'>resize</span> note";
  let td18_2 = document.createElement("td");
  td18_2.innerHTML = "Press the right side of note and move to right";
  row18.appendChild(td18_1); row18.appendChild(td18_2);
  tblHelp.appendChild(row18);

  let row19 = document.createElement("tr");
  let td19_1 = document.createElement("td");
  td19_1.innerHTML = "<span style='color: blue'>group</span> notes";
  let td19_2 = document.createElement("td");
  td19_2.innerHTML = "Select a track, select group notes and slide them left/right or up/down";
  row19.appendChild(td19_1); row19.appendChild(td19_2);
  tblHelp.appendChild(row19);

  let row20 = document.createElement("tr");
  let td20_1 = document.createElement("td");
  td20_1.innerHTML = "copy/paste";
  let td20_2 = document.createElement("td");
  td20_2.innerHTML = "When you execute the <b>group notes</b> command, the selected area with notes is saved in long-term <br>memory. Therefore, you can insert these notes into any midi file, anywhere - by pointing with <br>the mouse cursor, by clicking the right mouse button while holding down the <b>alt</b> key.<br>Before inserting a group of notes, you need to select the track on which the insertion occurs.";
  row20.appendChild(td20_1); row20.appendChild(td20_2);
  tblHelp.appendChild(row20);

  let row21 = document.createElement("tr");
  let td21_1 = document.createElement("td");
  td21_1.setAttribute("colspan", 2);
  td21_1.setAttribute("style", "text-align: center;background: #E3E3E3");
  td21_1.innerHTML = "<b>Edit info on selected track</b>";
  row21.appendChild(td21_1);
  tblHelp.appendChild(row21);

  let row22 = document.createElement("tr");
  let td22_1 = document.createElement("td");
  td22_1.innerHTML = "Track";
  let td22_2 = document.createElement("td");
  td22_2.innerHTML = "edit fields: Track name, Instrument name, Channel track";
  row22.appendChild(td22_1); row22.appendChild(td22_2);
  tblHelp.appendChild(row22);

  let row23 = document.createElement("tr");
  let td23_1 = document.createElement("td");
  td23_1.setAttribute("colspan", 2);
  td23_1.setAttribute("style", "text-align: center;background: #E3E3E3");
  td23_1.innerHTML = "<b>Context menu mouse on the timeScale</b>";
  row23.appendChild(td23_1);
  tblHelp.appendChild(row23);

  let row24 = document.createElement("tr");
  let td24_1 = document.createElement("td");
  td24_1.innerHTML = "Add timeSignature";
  let td24_2 = document.createElement("td");
  td24_2.innerHTML = "Insert a new time signature at the selected location";
  row24.appendChild(td24_1); row24.appendChild(td24_2);
  tblHelp.appendChild(row24);

  let row25 = document.createElement("tr");
  let td25_1 = document.createElement("td");
  td25_1.innerHTML = "Del timeSignature";
  let td25_2 = document.createElement("td");
  td25_2.innerHTML = "Delete the time signature at the selected location";
  row25.appendChild(td25_1); row25.appendChild(td25_2);
  tblHelp.appendChild(row25);

  let row26 = document.createElement("tr");
  let td26_1 = document.createElement("td");
  td26_1.innerHTML = "Edit timeSignature";
  let td26_2 = document.createElement("td");
  td26_2.innerHTML = "Edit the time signature at the selected location";
  row26.appendChild(td26_1); row26.appendChild(td26_2);
  tblHelp.appendChild(row26);

  let row27 = document.createElement("tr");
  let td27_1 = document.createElement("td");
  td27_1.innerHTML = "Add measure";
  let td27_2 = document.createElement("td");
  td27_2.innerHTML = "Add an empty bar to the selected track, or to all tracks";
  row27.appendChild(td27_1); row27.appendChild(td27_2);
  tblHelp.appendChild(row27);

  let row28 = document.createElement("tr");
  let td28_1 = document.createElement("td");
  td28_1.innerHTML = "Del measure";
  let td28_2 = document.createElement("td");
  td28_2.innerHTML = "Delete notes from a bar, a selected track, or all tracks";
  row28.appendChild(td28_1); row28.appendChild(td28_2);
  tblHelp.appendChild(row28);

  let row29 = document.createElement("tr");
  let td29_1 = document.createElement("td");
  td29_1.innerHTML = "Add BPM";
  let td29_2 = document.createElement("td");
  td29_2.innerHTML = "Add a new BPM at the selected location";
  row29.appendChild(td29_1); row29.appendChild(td29_2);
  tblHelp.appendChild(row29);

  let row30 = document.createElement("tr");
  let td30_1 = document.createElement("td");
  td30_1.innerHTML = "Del BPM";
  let td30_2 = document.createElement("td");
  td30_2.innerHTML = "Delete BPM at the selected location";
  row30.appendChild(td30_1); row30.appendChild(td30_2);
  tblHelp.appendChild(row30);

  let row31 = document.createElement("tr");
  let td31_1 = document.createElement("td");
  td31_1.innerHTML = "Edit BPM";
  let td31_2 = document.createElement("td");
  td31_2.innerHTML = "Edit BPM at the selected location";
  row31.appendChild(td31_1); row31.appendChild(td31_2);
  tblHelp.appendChild(row31);

  //MIDI CC List for Continuous Controllers:
  //https://anotherproducer.com/online-tools-for-musicians/midi-cc-list/

  let tblHelpTools = document.createElement("table");
  tblHelpTools.id = "tbl_help_tools";

  let row32 = document.createElement("tr");
  let td32_1 = document.createElement("td");
  td32_1.setAttribute("colspan", 3);
  td32_1.setAttribute("style", "text-align: center;background: #E3E3E3");
  td32_1.innerHTML = "<b>Tools</b>";
  row32.appendChild(td32_1);
  tblHelpTools.appendChild(row32);

  let row33 = document.createElement("tr");
  let td33_1 = document.createElement("th");
  td33_1.innerHTML = "Button";
  let td33_2 = document.createElement("th");
  td33_2.innerHTML = "MIDI CC#";
  let td33_3 = document.createElement("th");
  td33_3.innerHTML = "Action";
  row33.appendChild(td33_1); row33.appendChild(td33_2); row33.appendChild(td33_3);
  tblHelpTools.appendChild(row33);

  let row34 = document.createElement("tr");
  let td34_1 = document.createElement("td");
  td34_1.innerHTML = "Velocity";
  td34_1.setAttribute("style","width:80px;");
  let td34_2 = document.createElement("td");
  td34_2.innerHTML = "MIDI Event<br>type: 8<br>subtype: 9<br>param2";
  td34_2.setAttribute("style","width:80px;");
  let td34_3 = document.createElement("td");
  td34_3.innerHTML = "The rate at which a note rises or the force with which a musical key is pressed";
  row34.appendChild(td34_1); row34.appendChild(td34_2); row34.appendChild(td34_3);
  tblHelpTools.appendChild(row34);

  let row35 = document.createElement("tr");
  let td35_1 = document.createElement("td");
  td35_1.innerHTML = "Volume";
  let td35_2 = document.createElement("td");
  td35_2.innerHTML = "MIDI CC7";
  let td35_3 = document.createElement("td");
  td35_3.innerHTML = "Controls the volume of the channel.";
  row35.appendChild(td35_1); row35.appendChild(td35_2); row35.appendChild(td35_3);
  tblHelpTools.appendChild(row35);

  let row36 = document.createElement("tr");
  let td36_1 = document.createElement("td");
  td36_1.innerHTML = "Pitch Bend";
  let td36_2 = document.createElement("td");
  td36_2.innerHTML = "MIDI Event<br>type: 8<br>subtype: 14<br>param2";
  let td36_3 = document.createElement("td");
  td36_3.innerHTML = "(Pitch Wheel Change) Change the pitch up/down. The change range is 2 semitones up/down";
  row36.appendChild(td36_1); row36.appendChild(td36_2); row36.appendChild(td36_3);
  tblHelpTools.appendChild(row36);

  let row37 = document.createElement("tr");
  let td37_1 = document.createElement("td");
  td37_1.innerHTML = "Panpos";
  let td37_2 = document.createElement("td");
  td37_2.innerHTML = "MIDI CC10";
  let td37_3 = document.createElement("td");
  td37_3.innerHTML = "Controls left & right balance, generally for mono patches. A value of 64 equals the center.";
  row37.appendChild(td37_1); row37.appendChild(td37_2); row37.appendChild(td37_3);
  tblHelpTools.appendChild(row37);

  let row38 = document.createElement("tr");
  let td38_1 = document.createElement("td");
  td38_1.innerHTML = "Expression";
  let td38_2 = document.createElement("td");
  td38_2.innerHTML = "MIDI CC11";
  let td38_3 = document.createElement("td");
  td38_3.innerHTML = "Set expression level. Val range is 0-127. Both Expression and channel volume are control the channel volume, but Expression is mainly used as note's articulation.";
  row38.appendChild(td38_1); row38.appendChild(td38_2); row38.appendChild(td38_3);
  tblHelpTools.appendChild(row38);

  let row39 = document.createElement("tr");
  let td39_1 = document.createElement("td");
  td39_1.innerHTML = "Modulation";
  let td39_2 = document.createElement("td");
  td39_2.innerHTML = "MIDI CC1";
  let td39_3 = document.createElement("td");
  td39_3.innerHTML = "Set modulation (vibrato) depth. Val range is 0-127. +- 100 cent depth if val=127.";
  row39.appendChild(td39_1); row39.appendChild(td39_2); row39.appendChild(td39_3);
  tblHelpTools.appendChild(row39);

  let row40 = document.createElement("tr");
  let td40_1 = document.createElement("td");
  td40_1.innerHTML = "SoftPedal";
  let td40_2 = document.createElement("td");
  td40_2.innerHTML = "MIDI CC67";
  let td40_3 = document.createElement("td");
  td40_3.innerHTML = "On/off switch. Lowers the volume of notes played.";
  row40.appendChild(td40_1); row40.appendChild(td40_2); row40.appendChild(td40_3);
  tblHelpTools.appendChild(row40);

  let row41 = document.createElement("tr");
  let td41_1 = document.createElement("td");
  td41_1.innerHTML = "Sostenuto";
  let td41_2 = document.createElement("td");
  td41_2.innerHTML = "MIDI CC66";
  let td41_3 = document.createElement("td");
  td41_3.innerHTML = "On/off switch – Like the Sustain controller (CC 64), However, it only holds notes that were “On” when the pedal was pressed. People use it to “hold” chords” and play melodies over the held chord.";
  row41.appendChild(td41_1); row41.appendChild(td41_2); row41.appendChild(td41_3);
  tblHelpTools.appendChild(row41);

  let row42 = document.createElement("tr");
  let td42_1 = document.createElement("td");
  td42_1.innerHTML = "Sustain";
  let td42_2 = document.createElement("td");
  td42_2.innerHTML = "MIDI CC64";
  let td42_3 = document.createElement("td");
  td42_3.innerHTML = "Set sustain pedal state. While sustain is on, generated notes in that channel are sustained even corresponding noteOff() is called. Note that val is judged as 'on' if val>=64. Usually use 0 and 127 as a value.";
  row42.appendChild(td42_1); row42.appendChild(td42_2); row42.appendChild(td42_3);
  tblHelpTools.appendChild(row42);

  let row43 = document.createElement("tr");
  let td43_1 = document.createElement("td");
  td43_1.innerHTML = "Balance";
  let td43_2 = document.createElement("td");
  td43_2.innerHTML = "MIDI CC8";
  let td43_3 = document.createElement("td");
  td43_3.innerHTML = "Controls left & right balance, generally for stereo patches. A value of 64 equals the center.";
  row43.appendChild(td43_1); row43.appendChild(td43_2); row43.appendChild(td43_3);
  tblHelpTools.appendChild(row43);



  let tblCommonCtrl = document.createElement("table");
  tblCommonCtrl.id = "tbl_common_ctrl";

  let row44 = document.createElement("tr");
  let td44_1 = document.createElement("td");
  td44_1.setAttribute("colspan", 2);
  td44_1.setAttribute("style", "text-align: center;background: #E3E3E3");
  td44_1.innerHTML = "<b>Common control</b>";
  row44.appendChild(td44_1);
  tblCommonCtrl.appendChild(row44);

  let row45 = document.createElement("tr");
  let td45_1 = document.createElement("th");
  td45_1.innerHTML = "Item";
  let td45_2 = document.createElement("th");
  td45_2.innerHTML = "Action";
  row45.appendChild(td45_1); row45.appendChild(td45_2);
  tblCommonCtrl.appendChild(row45);

  let row46 = document.createElement("tr");
  let td46_1 = document.createElement("td");
  td46_1.innerHTML = "Use SoundFonts2";
  td46_1.setAttribute("style","width:80px;");
  let td46_2 = document.createElement("td");
  td46_2.innerHTML = "1.Click the button <b>Settings</b>.<br>2.You will see two tables:<br><b>Sound Fonts GS</b> and <b>Sound Fonts XG</b><br>3.These tables can be filled with files *.sf2 - If the administrator of the site where this plugin is installed has kindly provided you with his files .sf2 - standards GS, XG.<br>4.You can use your *.sf2 files by clicking the button <b>Add</b><br>5.Set the selector of the selected files in each table and click the button <b>Save</b><br>6.Selectors will appear in the upper right corner of the synth. <b>GS</b> и <b>XG</b>. Use them.";
  td46_2.setAttribute("style","width:80px;");
  row46.appendChild(td46_1); row46.appendChild(td46_2);
  tblCommonCtrl.appendChild(row46);


  div.appendChild(tblHelp);
  div.appendChild(tblHelpTools);
  div.appendChild(tblCommonCtrl);
}
function addTrack(){

  btnTool = "";

  if("" == selTrackNum) return;
  midiFile.addTrack(selTrackNum);
  let src = midiFile.getContent();
  synth.loadMIDI(src);
  Sequencer();
}
function delTrack(){

  btnTool = "";

  if("" == selTrackNum) return;
  midiFile.delTrack(selTrackNum);
  let src = midiFile.getContent();
  synth.loadMIDI(src);
  Sequencer();
}
/**
 * Sends a request to the conversion server
 * Get master API key: https://account.api2convert.com/user/apikeys
 */
function midiConvert(){
  var fileName = document.getElementById("file_name_midi").value;
  var fileSize = document.getElementById("file_size_midi").value;
  var responseConvert = document.getElementById("responseConvert");

  var formatAudio     = document.querySelectorAll("[name='formatAudio']:checked")[0].value;
  var modeAudio       = document.querySelectorAll("[name='modeAudio']:checked")[0].value;
  var codecAudio      = document.querySelectorAll("[name='codecAudio']:checked")[0].value;
  var frequencyAudio  = document.querySelectorAll("[name='frequencyAudio']:checked")[0].value;
  var resolutionAudio = document.querySelectorAll("[name='resolutionAudio']:checked")[0].value;
  var bitrateAudio    = document.querySelectorAll("[name='bitrateAudio']:checked")[0].value;

  responseConvert.innerHTML = "<b>STEP 1: Update MIDI file:</b><br>"

  var masterAPIkey = localStorage.getItem("masterAPIkey");

  if(null == masterAPIkey || "" == masterAPIkey){
    openSetting("btn_setting");
    return;
  }

  var fileMidi = fileMidiConvert();

  if(-1 !== fileName.indexOf(".")){
    var arr = fileName.split(".");
    fileName = arr[0] + ".mid";
  }else{
    fileName += ".mid";
  }

  responseConvert.innerHTML += "fileName: "+fileName+"<br>fileSize: "+fileSize+"<br>";
  var progressBar = createProgressBar(responseConvert);
  responseConvert.appendChild(progressBar);

  var xhr = new XMLHttpRequest();
  var formData = new FormData();

  formData.append("action", "export");
  formData.append("nonce", midiSynth_nonce);

  formData.append("masterAPIkey", masterAPIkey);
  formData.append("fileMidi", fileMidi);
  formData.append("fileName", fileName);
  formData.append("fileSize", fileSize);
  formData.append("formatAudio", formatAudio);
  formData.append("modeAudio", modeAudio);
  formData.append("codecAudio", codecAudio);
  formData.append("frequencyAudio", frequencyAudio);
  formData.append("resolutionAudio", resolutionAudio);
  formData.append("bitrateAudio", bitrateAudio);

  xhr.open("POST", midiSynth_url_ajax);
  xhr.send(formData);

  xhr.onreadystatechange = function(){
    if (this.readyState == 4 && this.status == 200) {
      if(progressBar) responseConvert.removeChild(progressBar);
      responseConvert.innerHTML += this.responseText;
      responseConvert.scrollTo(0, responseConvert.scrollHeight);
    }
  };
  xhr.onerror = function(){
    if(progressBar) responseConvert.removeChild(progressBar);
    responseConvert.innerHTML = this.responseText;
  };
}
/**
 * For Export fileMidi ProgressBar
 * @param  {[type]} responseConvert [description]
 * @return {[type]}                 [description]
 */
function createProgressBar(responseConvert){
  let divProgress = document.createElement("div");
  divProgress.id="progress_connection";

  let div_sk_wave = document.createElement("div");
  div_sk_wave.innerHTML = "wait: ";
  div_sk_wave.setAttribute("class", "sk-wave");

  let div_sk_rect1 = document.createElement("div");
  div_sk_rect1.setAttribute("class", "sk-rect sk-rect-1");
  let div_sk_rect2 = document.createElement("div");
  div_sk_rect2.setAttribute("class", "sk-rect sk-rect-2");
  let div_sk_rect3 = document.createElement("div");
  div_sk_rect3.setAttribute("class", "sk-rect sk-rect-3");
  let div_sk_rect4 = document.createElement("div");
  div_sk_rect4.setAttribute("class", "sk-rect sk-rect-4");
  let div_sk_rect5 = document.createElement("div");
  div_sk_rect5.setAttribute("class", "sk-rect sk-rect-5");

  div_sk_wave.appendChild(div_sk_rect1);
  div_sk_wave.appendChild(div_sk_rect2);
  div_sk_wave.appendChild(div_sk_rect3);
  div_sk_wave.appendChild(div_sk_rect4);
  div_sk_wave.appendChild(div_sk_rect5);

  divProgress.appendChild(div_sk_wave);

  return divProgress;
}

/**
 * Generates parameters for a request to the conversion server
 * https://audio.online-convert.com/
 */
function exportMid(){
  let div  = document.getElementById("stave");

  let masterAPIkey = localStorage.getItem("masterAPIkey");
  if("" == masterAPIkey){
    let msg = document.createElement("label");
    msg.setAttribute("style","display:flex;width:100%;font-size:22px;font-weight: bold;color:red;justify-content: center;align-items: center;");
    msg.innerHTML = "Click the Setting button and save the resulting 'Master API key'";

    div.appendChild(msg);

    return;
  }

  let tblExport = document.createElement("table");
  tblExport.id  = "tbl_export";

  let tr1 = document.createElement("tr");
  tblExport.appendChild(tr1);

  let th1 = document.createElement("th");
  th1.setAttribute("style","padding:0;");
  th1.innerHTML = "Format";
  tr1.appendChild(th1);

  let th2 = document.createElement("th");
  th2.setAttribute("style","padding:0;");
  th2.innerHTML = "Mode";
  tr1.appendChild(th2);

  let th3 = document.createElement("th");
  th3.setAttribute("style","padding:0;");
  th3.innerHTML = "Codec";
  tr1.appendChild(th3);

  let th4 = document.createElement("th");
  th4.setAttribute("style","padding:0;");
  th4.innerHTML = "Frequency";
  tr1.appendChild(th4);

  let th5 = document.createElement("th");
  th5.setAttribute("style","padding:0;");
  th5.innerHTML = "Resolution";
  tr1.appendChild(th5);

  let th6 = document.createElement("th");
  th6.setAttribute("style","padding:0;");
  th6.innerHTML = "Bitrate";
  tr1.appendChild(th6);

  let th7 = document.createElement("th");
  th7.setAttribute("style","padding:0;display: flex;");
  th7.innerHTML = "<button class='btn_sequencer' id='btn_request' style='height:22px;' onclick='midiConvert();'>Request</button>";
  tr1.appendChild(th7);

  let imgLogoExport = new Image(140, 20);
  imgLogoExport.src = online_convert.src;

  imgLogoExport.setAttribute("style", "display:block;margin-top:2px;height: 22px;cursor:pointer;cursor:pointer;border: 1px solid gray;");
  imgLogoExport.onclick = () => {
    window.open("https://audio.online-convert.com/", "_blank");
  };
  th7.appendChild(imgLogoExport);

  let tr2 = document.createElement("tr");
  tblExport.appendChild(tr2);

  let td1 = document.createElement("td");
  td1.setAttribute("style","padding:5px;width:70px;");
  td1.id = "format";
  //---------------------------------------------
  let in1 = document.createElement("input");
  in1.setAttribute("name", "formatAudio");
  in1.setAttribute("type", "radio");
  in1.setAttribute("value", "mp3");
  in1.setAttribute("onclick", "changeParamsAudioTd(value)");
  in1.checked = true;
  in1.id = "convert1";

  let lbl1 = document.createElement("label");
  lbl1.setAttribute("style", "margin-left:5px;");
  lbl1.setAttribute("for", "convert1");
  lbl1.innerHTML = "MP3";

  let div1 = document.createElement("div");
  div1.setAttribute("style", "height: 20px;");
  div1.appendChild(in1);
  div1.appendChild(lbl1);
  td1.appendChild(div1);
  //---------------------------------------------
  let in2 = document.createElement("input");
  in2.setAttribute("name", "formatAudio");
  in2.setAttribute("type", "radio");
  in2.setAttribute("value", "wav");
  in2.setAttribute("onclick", "changeParamsAudioTd(value)");
  in2.id = "convert2";

  let lbl2 = document.createElement("label");
  lbl2.setAttribute("style", "margin-left:5px;");
  lbl2.setAttribute("for", "convert2");
  lbl2.innerHTML = "WAV";

  let div2 = document.createElement("div");
  div2.setAttribute("style", "height: 20px;");
  div2.appendChild(in2);
  div2.appendChild(lbl2);
  td1.appendChild(div2);
  //---------------------------------------------
  let in3 = document.createElement("input");
  in3.setAttribute("name", "formatAudio");
  in3.setAttribute("type", "radio");
  in3.setAttribute("value", "wma");
  in3.setAttribute("onclick", "changeParamsAudioTd(value)");
  in3.id = "convert3";

  let lbl3 = document.createElement("label");
  lbl3.setAttribute("style", "margin-left:5px;");
  lbl3.setAttribute("for", "convert3");
  lbl3.innerHTML = "WMA";

  let div3 = document.createElement("div");
  div3.setAttribute("style", "height: 20px;");
  div3.appendChild(in3);
  div3.appendChild(lbl3);
  td1.appendChild(div3);
  //---------------------------------------------
  let in4 = document.createElement("input");
  in4.setAttribute("name", "formatAudio");
  in4.setAttribute("type", "radio");
  in4.setAttribute("value", "m4a");
  in4.setAttribute("onclick", "changeParamsAudioTd(value)");
  in4.id = "convert4";

  let lbl4 = document.createElement("label");
  lbl4.setAttribute("style", "margin-left:5px;");
  lbl4.setAttribute("for", "convert4");
  lbl4.innerHTML = "M4A";

  let div4 = document.createElement("div");
  div4.setAttribute("style", "height: 20px;");
  div4.appendChild(in4);
  div4.appendChild(lbl4);
  td1.appendChild(div4);
  //---------------------------------------------
  let in5 = document.createElement("input");
  in5.setAttribute("name", "formatAudio");
  in5.setAttribute("type", "radio");
  in5.setAttribute("value", "aac");
  in5.setAttribute("onclick", "changeParamsAudioTd(value)");
  in5.id = "convert5";

  let lbl5 = document.createElement("label");
  lbl5.setAttribute("style", "margin-left:5px;");
  lbl5.setAttribute("for", "convert5");
  lbl5.innerHTML = "AAC";

  let div5 = document.createElement("div");
  div5.setAttribute("style", "height: 20px;");
  div5.appendChild(in5);
  div5.appendChild(lbl5);
  td1.appendChild(div5);
  //---------------------------------------------
  let in6 = document.createElement("input");
  in6.setAttribute("name", "formatAudio");
  in6.setAttribute("type", "radio");
  in6.setAttribute("value", "flac");
  in6.setAttribute("onclick", "changeParamsAudioTd(value)");
  in6.id = "convert6";

  let lbl6 = document.createElement("label");
  lbl6.setAttribute("style", "margin-left:5px;");
  lbl6.setAttribute("for", "convert6");
  lbl6.innerHTML = "FLAC";

  let div6 = document.createElement("div");
  div6.setAttribute("style", "height: 20px;");
  div6.appendChild(in6);
  div6.appendChild(lbl6);
  td1.appendChild(div6);
  //=============================================

  tr2.appendChild(td1);

  let td2 = document.createElement("td");
  td2.setAttribute("style","padding:5px;width:80px;");
  td2.id = "mode";
  tr2.appendChild(td2);

  let in21 = document.createElement("input");
  in21.setAttribute("name", "modeAudio");
  in21.setAttribute("type", "radio");
  in21.setAttribute("value", "mono");
  in21.checked = true;
  in21.id = "mode1";

  let lbl21 = document.createElement("label");
  lbl21.setAttribute("style", "margin-left:5px;");
  lbl21.setAttribute("for", "mode1");
  lbl21.innerHTML = "mono";

  let div21 = document.createElement("div");
  div21.setAttribute("style", "height: 20px;");
  div21.appendChild(in21);
  div21.appendChild(lbl21);
  td2.appendChild(div21);
  //--------------------------------------------
  let in22 = document.createElement("input");
  in22.setAttribute("name", "modeAudio");
  in22.setAttribute("type", "radio");
  in22.setAttribute("value", "stereo");
  in22.id = "mode2";

  let lbl22 = document.createElement("label");
  lbl22.setAttribute("style", "margin-left:5px;");
  lbl22.setAttribute("for", "mode2");
  lbl22.innerHTML = "stereo";

  let div22 = document.createElement("div");
  div22.setAttribute("style", "height: 20px;");
  div22.appendChild(in22);
  div22.appendChild(lbl22);
  td2.appendChild(div22);
  //===============================================
  let td3 = document.createElement("td");
  td3.setAttribute("style","padding:5px;width:80px;");
  td3.id = "codecs";
  tr2.appendChild(td3);

  let in31 = document.createElement("input");
  in31.setAttribute("name", "codecAudio");
  in31.setAttribute("type", "radio");
  in31.setAttribute("value", "");
  in31.checked = true;
  in31.id = "codec1";

  let lbl31 = document.createElement("label");
  lbl31.setAttribute("style", "margin-left:5px;");
  lbl31.setAttribute("for", "codec1");
  lbl31.id = "lbl_codec1";
  lbl31.innerHTML = "";

  let div31 = document.createElement("div");
  div31.setAttribute("style", "height: 20px;");
  div31.appendChild(in31);
  div31.appendChild(lbl31);
  td3.appendChild(div31);
  //--------------------------------------------
  let in32 = document.createElement("input");
  in32.setAttribute("name", "codecAudio");
  in32.setAttribute("type", "radio");
  in32.setAttribute("value", "");
  in32.id = "codec2";

  let lbl32 = document.createElement("label");
  lbl32.setAttribute("style", "margin-left:5px;");
  lbl32.setAttribute("for", "codec2");
  lbl32.id = "lbl_codec2";
  lbl32.innerHTML = "";

  let div32 = document.createElement("div");
  div32.setAttribute("style", "height: 20px;");
  div32.appendChild(in32);
  div32.appendChild(lbl32);
  td3.appendChild(div32);
  //--------------------------------------------
  let in33 = document.createElement("input");
  in33.setAttribute("name", "codecAudio");
  in33.setAttribute("type", "radio");
  in33.setAttribute("value", "");
  in33.id = "codec3";

  let lbl33 = document.createElement("label");
  lbl33.setAttribute("style", "margin-left:5px;");
  lbl33.setAttribute("for", "codec3");
  lbl33.id = "lbl_codec3";
  lbl33.innerHTML = "";

  let div33 = document.createElement("div");
  div33.setAttribute("style", "height: 20px;");
  div33.appendChild(in33);
  div33.appendChild(lbl33);
  td3.appendChild(div33);
  //============================================
  let td4 = document.createElement("td");
  td4.setAttribute("style","padding:5px;width:105px;");
  td4.id = "frequency";
  tr2.appendChild(td4);

  let in41 = document.createElement("input");
  in41.setAttribute("name", "frequencyAudio");
  in41.setAttribute("type", "radio");
  in41.setAttribute("value", 8000);
  in41.checked = true;
  in41.id = "frequency1";

  let lbl41 = document.createElement("label");
  lbl41.setAttribute("style", "margin-left:5px;");
  lbl41.setAttribute("for", "frequency1");
  lbl41.innerHTML = "8000 Hz";

  let div41 = document.createElement("div");
  div41.setAttribute("style", "height: 20px;");
  div41.appendChild(in41);
  div41.appendChild(lbl41);
  td4.appendChild(div41);
  //--------------------------------------------
  let in42 = document.createElement("input");
  in42.setAttribute("name", "frequencyAudio");
  in42.setAttribute("type", "radio");
  in42.setAttribute("value", 16000);
  in42.id = "frequency2";

  let lbl42 = document.createElement("label");
  lbl42.setAttribute("style", "margin-left:5px;");
  lbl42.setAttribute("for", "frequency2");
  lbl42.innerHTML = "16000 Hz";

  let div42 = document.createElement("div");
  div42.setAttribute("style", "height: 20px;");
  div42.appendChild(in42);
  div42.appendChild(lbl42);
  td4.appendChild(div42);
  //--------------------------------------------
  let in43 = document.createElement("input");
  in43.setAttribute("name", "frequencyAudio");
  in43.setAttribute("type", "radio");
  in43.setAttribute("value", 22050);
  in43.id = "frequency3";

  let lbl43 = document.createElement("label");
  lbl43.setAttribute("style", "margin-left:5px;");
  lbl43.setAttribute("for", "frequency3");
  lbl43.innerHTML = "22050 Hz";

  let div43 = document.createElement("div");
  div43.setAttribute("style", "height: 20px;");
  div43.appendChild(in43);
  div43.appendChild(lbl43);
  td4.appendChild(div43);
  //--------------------------------------------
  let in44 = document.createElement("input");
  in44.setAttribute("name", "frequencyAudio");
  in44.setAttribute("type", "radio");
  in44.setAttribute("value", 24000);
  in44.id = "frequency4";

  let lbl44 = document.createElement("label");
  lbl44.setAttribute("style", "margin-left:5px;");
  lbl44.setAttribute("for", "frequency4");
  lbl44.innerHTML = "24000 Hz";

  let div44 = document.createElement("div");
  div44.setAttribute("style", "height: 20px;");
  div44.appendChild(in44);
  div44.appendChild(lbl44);
  td4.appendChild(div44);
  //--------------------------------------------
  let in45 = document.createElement("input");
  in45.setAttribute("name", "frequencyAudio");
  in45.setAttribute("type", "radio");
  in45.setAttribute("value", 32000);
  in45.id = "frequency5";

  let lbl45 = document.createElement("label");
  lbl45.setAttribute("style", "margin-left:5px;");
  lbl45.setAttribute("for", "frequency5");
  lbl45.innerHTML = "32000 Hz";

  let div45 = document.createElement("div");
  div45.setAttribute("style", "height: 20px;");
  div45.appendChild(in45);
  div45.appendChild(lbl45);
  td4.appendChild(div45);
  //--------------------------------------------
  let in46 = document.createElement("input");
  in46.setAttribute("name", "frequencyAudio");
  in46.setAttribute("type", "radio");
  in46.setAttribute("value", 48000);
  in46.id = "frequency6";

  let lbl46 = document.createElement("label");
  lbl46.setAttribute("style", "margin-left:5px;");
  lbl46.setAttribute("for", "frequency6");
  lbl46.innerHTML = "48000 Hz";

  let div46 = document.createElement("div");
  div46.setAttribute("style", "height: 20px;");
  div46.appendChild(in46);
  div46.appendChild(lbl46);
  td4.appendChild(div46);
  //==============================================
  let td5 = document.createElement("td");
  td5.setAttribute("style","padding:5px;width:100px;");
  td5.id = "resolution";
  tr2.appendChild(td5);

  let in51 = document.createElement("input");
  in51.setAttribute("name", "resolutionAudio");
  in51.setAttribute("type", "radio");
  in51.setAttribute("value", "8");
  in51.checked = true;
  in51.id = "resolution1";

  let lbl51 = document.createElement("label");
  lbl51.setAttribute("style", "margin-left:5px;");
  lbl51.setAttribute("for", "resolution1");
  lbl51.id = "lbl_resolution1";
  lbl51.innerHTML = "8 Bit";

  let div51 = document.createElement("div");
  div51.setAttribute("style", "height: 20px;");
  div51.appendChild(in51);
  div51.appendChild(lbl51);
  td5.appendChild(div51);
  //--------------------------------------------
  let in52 = document.createElement("input");
  in52.setAttribute("name", "resolutionAudio");
  in52.setAttribute("type", "radio");
  in52.setAttribute("value", "16");
  in52.id = "resolution2";

  let lbl52 = document.createElement("label");
  lbl52.setAttribute("style", "margin-left:5px;");
  lbl52.setAttribute("for", "resolution2");
  lbl52.id = "lbl_resolution2";
  lbl52.innerHTML = "16 Bit";

  let div52 = document.createElement("div");
  div52.setAttribute("style", "height: 20px;");
  div52.appendChild(in52);
  div52.appendChild(lbl52);
  td5.appendChild(div52);
  //--------------------------------------------
  let in53 = document.createElement("input");
  in53.setAttribute("name", "resolutionAudio");
  in53.setAttribute("type", "radio");
  in53.setAttribute("value", "24");
  in53.id = "resolution3";

  let lbl53 = document.createElement("label");
  lbl53.setAttribute("style", "margin-left:5px;");
  lbl53.setAttribute("for", "resolution3");
  lbl53.id = "lbl_resolution3";
  lbl53.innerHTML = "24 Bit";

  let div53 = document.createElement("div");
  div53.setAttribute("style", "height: 20px;");
  div53.appendChild(in53);
  div53.appendChild(lbl53);
  td5.appendChild(div53);
  //--------------------------------------------
  let in54 = document.createElement("input");
  in54.setAttribute("name", "resolutionAudio");
  in54.setAttribute("type", "radio");
  in54.setAttribute("value", "32");
  in54.id = "resolution4";

  let lbl54 = document.createElement("label");
  lbl54.setAttribute("style", "margin-left:5px;");
  lbl54.setAttribute("for", "resolution4");
  lbl54.id = "lbl_resolution4";
  lbl54.innerHTML = "32 Bit";

  let div54 = document.createElement("div");
  div54.setAttribute("style", "height: 20px;");
  div54.appendChild(in54);
  div54.appendChild(lbl54);
  td5.appendChild(div54);
  //==============================================

  let td6 = document.createElement("td");
  td6.setAttribute("style","padding:5px;width:110px;");
  td6.id = "bitrates";
  tr2.appendChild(td6);

  let in61 = document.createElement("input");
  in61.setAttribute("name", "bitrateAudio");
  in61.setAttribute("type", "radio");
  in61.setAttribute("value", 24);
  in61.checked = true;
  in61.id = "bitrate1";

  let lbl61 = document.createElement("label");
  lbl61.setAttribute("style", "margin-left:5px;");
  lbl61.setAttribute("for", "bitrate1");
  lbl61.innerHTML = "24 kb/sec";

  let div61 = document.createElement("div");
  div61.setAttribute("style", "height: 20px;");
  div61.appendChild(in61);
  div61.appendChild(lbl61);
  td6.appendChild(div61);
  //--------------------------------------------
  let in62 = document.createElement("input");
  in62.setAttribute("name", "bitrateAudio");
  in62.setAttribute("type", "radio");
  in62.setAttribute("value", 48);
  in62.id = "bitrate2";

  let lbl62 = document.createElement("label");
  lbl62.setAttribute("style", "margin-left:5px;");
  lbl62.setAttribute("for", "bitrate2");
  lbl62.innerHTML = "48 kb/sec";

  let div62 = document.createElement("div");
  div62.setAttribute("style", "height: 20px;");
  div62.appendChild(in62);
  div62.appendChild(lbl62);
  td6.appendChild(div62);
  //--------------------------------------------
  let in63 = document.createElement("input");
  in63.setAttribute("name", "bitrateAudio");
  in63.setAttribute("type", "radio");
  in63.setAttribute("value", 96);
  in63.id = "bitrate3";

  let lbl63 = document.createElement("label");
  lbl63.setAttribute("style", "margin-left:5px;");
  lbl63.setAttribute("for", "bitrate3");
  lbl63.innerHTML = "96 kb/sec";

  let div63 = document.createElement("div");
  div63.setAttribute("style", "height: 20px;");
  div63.appendChild(in63);
  div63.appendChild(lbl63);
  td6.appendChild(div63);
  //--------------------------------------------
  let in64 = document.createElement("input");
  in64.setAttribute("name", "bitrateAudio");
  in64.setAttribute("type", "radio");
  in64.setAttribute("value", 128);
  in64.id = "bitrate4";

  let lbl64 = document.createElement("label");
  lbl64.setAttribute("style", "margin-left:5px;");
  lbl64.setAttribute("for", "bitrate4");
  lbl64.innerHTML = "128 kb/sec";

  let div64 = document.createElement("div");
  div64.setAttribute("style", "height: 20px;");
  div64.appendChild(in64);
  div64.appendChild(lbl64);
  td6.appendChild(div64);
  //--------------------------------------------
  let in65 = document.createElement("input");
  in65.setAttribute("name", "bitrateAudio");
  in65.setAttribute("type", "radio");
  in65.setAttribute("value", 192);
  in65.id = "bitrate5";

  let lbl65 = document.createElement("label");
  lbl65.setAttribute("style", "margin-left:5px;");
  lbl65.setAttribute("for", "bitrate5");
  lbl65.innerHTML = "192 kb/sec";

  let div65 = document.createElement("div");
  div65.setAttribute("style", "height: 20px;");
  div65.appendChild(in65);
  div65.appendChild(lbl65);
  td6.appendChild(div65);
  //--------------------------------------------
  let in66 = document.createElement("input");
  in66.setAttribute("name", "bitrateAudio");
  in66.setAttribute("type", "radio");
  in66.setAttribute("value", 256);
  in66.id = "bitrate6";

  let lbl66 = document.createElement("label");
  lbl66.setAttribute("style", "margin-left:5px;");
  lbl66.setAttribute("for", "bitrate6");
  lbl66.innerHTML = "256 kb/sec";

  let div66 = document.createElement("div");
  div66.setAttribute("style", "height: 20px;");
  div66.appendChild(in66);
  div66.appendChild(lbl66);
  td6.appendChild(div66);
  //=============================================
  let td7 = document.createElement("td");
  let div7 = document.createElement("div");
  div7.setAttribute("style", "display: block;max-height: 160px;height: 160px;overflow-y: auto;padding:2px;background: #E3E3E3;line-height: 1.3;");
  div7.id = "responseConvert";
  td7.appendChild(div7);
  tr2.appendChild(td7);

  div.appendChild(tblExport);
  //Resetting to default state
  changeParamsAudioTd("mp3");
}
function changeParamsAudioTd(value){
  let itemCodecs = document.getElementById("codecs");
  let itemResolution = document.getElementById("resolution");
  let itemBitrates = document.getElementById("bitrates");

  if("wav" == value || "flac" == value){
    itemResolution.style.opacity = "1.0";
    document.getElementById("resolution1").value = 8;
    document.getElementById("lbl_resolution1").innerHTML = "8 Bit";
    document.getElementById("resolution2").value = 16;
    document.getElementById("lbl_resolution2").innerHTML = "16 Bit";
    document.getElementById("resolution3").value = 24;
    document.getElementById("lbl_resolution3").innerHTML = "24 Bit";
    document.getElementById("resolution4").value = 32;
    document.getElementById("lbl_resolution4").innerHTML = "32 Bit";
  }else{
    itemResolution.style.opacity = "0.1";
    document.getElementById("resolution1").value = "";
    document.getElementById("lbl_resolution1").innerHTML = "";
    document.getElementById("resolution2").value = "";
    document.getElementById("lbl_resolution2").innerHTML = "";
    document.getElementById("resolution3").value = "";
    document.getElementById("lbl_resolution3").innerHTML = "";
    document.getElementById("resolution4").value = "";
    document.getElementById("lbl_resolution4").innerHTML = "";
  }
  if("wav" == value || "flac" == value){
    itemBitrates.style.opacity = "0.1";
  }else{
    itemBitrates.style.opacity = "1.0";
  }
  if("wma" == value || "m4a" == value){
    itemCodecs.style.opacity = "1.0";
  }else{
    itemCodecs.style.opacity = "0.1";
    document.getElementById("codec1").value = "";
    document.getElementById("lbl_codec1").innerHTML = "";
    document.getElementById("codec2").value = "";
    document.getElementById("lbl_codec2").innerHTML = "";
    document.getElementById("codec3").value = "";
    document.getElementById("lbl_codec3").innerHTML = "";
  }
  if("wma" == value){
    document.getElementById("codec1").value = "wma1";
    document.getElementById("lbl_codec1").innerHTML = "wma1";
    document.getElementById("codec2").value = "wma2";
    document.getElementById("lbl_codec2").innerHTML = "wma2";
    document.getElementById("codec3").value = "copy";
    document.getElementById("lbl_codec3").innerHTML = "copy";
  }
  if("m4a" == value){
    document.getElementById("codec1").value = "aac";
    document.getElementById("lbl_codec1").innerHTML = "aac";
    document.getElementById("codec2").value = "alac";
    document.getElementById("lbl_codec2").innerHTML = "alac";
    document.getElementById("codec3").value = "copy";
    document.getElementById("lbl_codec3").innerHTML = "copy";
  }
}
function timerRecord(){
  let stave = document.getElementById("stave");
  let container = document.createElement("div");
  let timerRec = document.createElement("label");
  let timerTick = document.createElement("label");
  let timerMeasure = document.createElement("label");
  let timerSignature = document.createElement("label");

  container.setAttribute("style", "display:flex;justify-content:space-around;font-size:20px;width:600px;");

  timerRec.id = "timerRec";
  timerRec.setAttribute("style", "display:block;");
  timerRec.innerHTML = "time: 00:00";

  timerTick.id = "timerTick";
  timerTick.setAttribute("style", "display:block;");
  timerTick.innerHTML = "tick: 000";

  timerMeasure.id = "timerMeasure";
  timerMeasure.setAttribute("style", "display:block;");
  timerMeasure.innerHTML = "measure: 00";

  timerSignature.id = "timerSignature";
  timerSignature.setAttribute("style", "display:block;");
  timerSignature.innerHTML = "timeSignature: 0/0";

  container.appendChild(timerRec);
  container.appendChild(timerTick);
  container.appendChild(timerMeasure);
  container.appendChild(timerSignature);

  stave.appendChild(container);

  let maxTick  = synth.getSongDurationTicks();
  let tempoBPM = synth.getTempoBPM();
  let ppqn = synth.getPPQN();

  let rec_cur_measure = 1;
  let lengthBeat;
  let interval;

  let numerator;
  let denominator;

  arrTimeSignature = synth.getTimeSignature();

  arrTimeSignature.forEach(function(item, idx) {
    if(255 == item.type && 88 == item.subtype){
      if (2 == item.timeSignature[1]) lengthBeat = ppqn;
      if (3 == item.timeSignature[1]) lengthBeat = ppqn/2;
      if (4 == item.timeSignature[1]) lengthBeat = ppqn/4;
      if (5 == item.timeSignature[1]) lengthBeat = ppqn/8;

      numerator = item.timeSignature[0];
      denominator = item.timeSignature[1];
    }
  });

  timerSignature.innerHTML = "timeSignature: "+numerator+"/"+Math.pow(2, denominator);

  interval = Math.round((lengthBeat*60/ppqn/tempoBPM)*1000);

  //запуск таймера мин/сек
  timerRec.started = new Date;
  timerRec.update = ms => timerRec.innerHTML = "<b>time</b>: "+new Date(ms).toISOString().split(/T|\./)[1];

  let timeRecord = setInterval(() => {
    timerRec.update(new Date - timerRec.started);

    rec_cur_tick += lengthBeat/8;
    timerTick.innerHTML="<b>ticks</b>:"+rec_cur_tick;

    rec_cur_measure = Math.trunc(rec_cur_tick/(lengthBeat*numerator));
    timerMeasure.innerHTML = "<b>measure</b>:"+rec_cur_measure;

    //Current position of the sequencer pointer
    ptrTime.style.left=Math.trunc(rec_cur_tick/12)-8+"px";
    let box = ptrTime.getBoundingClientRect();
    if(1140 < box.left) subwin_part2.scrollLeft += 680;

    if("btn_record" !== pressBtn) clearInterval(timeRecord);
    if(rec_cur_tick > maxTick) {
      clearInterval(timeRecord);
      openRecord("btn_record");
    }
  }, interval/8);
}
async function verifyNotes(){
  let stave = document.getElementById("stave");
  let ppqn = synth.getPPQN();
  let arrNotes = document.getElementById("svg_body_part2");
  let tracksCount = document.getElementById("tracks_count").value;
  let arrNotesTrk = [];
  let arrIndex = [];
  let dublicates = [];
  let chunk = 0;
  let oldPosX = 0;
  let newPosX = 0;
  let posX = 0;
  let _posX = 0;
  let txt;
  let measure;

  function sleep1(ms, i, tracksCount) {
    document.getElementById("process").innerHTML = "Track:<br>" +i+" of "+tracksCount;
    return new Promise(resolve => setTimeout(resolve, ms));
  }
  function sleep2(ms, i, notesCount) {
    document.getElementById("process").innerHTML = "Note:<br>" +i+" of "+notesCount;
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  arrNotes = document.getElementById("svg_body_part2");

  for(let i=1; i <= tracksCount; i++){
    await sleep1(500, i, tracksCount);

    arrNotesTrk = arrNotes.querySelectorAll(`[trk="${i}"]`);

    //Sets the arrNotes of the entire song to a PPQN/4 grid.
    arrNotesTrk.forEach(function(note, idx){
      oldPosX = 0;
      newPosX = 0;
      chunk = 0;
      if("rect" == note.tagName) {
        oldPosX = parseInt(note.getAttribute("x"));
        chunk = oldPosX%(Math.round(ppqn/4/12));
        if(0 !== chunk){
          newPosX = oldPosX - chunk;
          note.setAttribute("x", newPosX);
          changedData = true;
        }
      }
      if("circle" == note.tagName) {
        oldPosX = parseInt(note.getAttribute("cx"));
        chunk = oldPosX%(Math.round(ppqn/4/12));
        if(0 !== chunk){
          newPosX = oldPosX - chunk;
          note.setAttribute("cx", newPosX);
          changedData = true;
        }
      }
      if(0 !== chunk) {
        for(let i=0; i < arrMeasures.length; i++){
          if(arrMeasures[i+1]){
            if(arrMeasures[i].tick <= newPosX*12 && arrMeasures[i+1].tick > newPosX*12){
              measure = arrMeasures[i].measure+1;
              break;
            }
          }else{
            if(arrMeasures[i].tick <= posX*12){
              measure = arrMeasures[i].measure+1;
              break;
            }
          }
        }
        txt = document.createElement("p");
        txt.setAttribute("style", "font-size:12px;height:16px;margin:0;");
        let chNum = parseInt(note.getAttribute("ch"))+1;
        let noteNum = parseInt(note.getAttribute("note"));
        let noteName = synth.arrNotes[noteNum];
        txt.innerHTML = "Element is placed in a grid node PPQN/4="+ppqn/4+
        " oldPosX="+oldPosX+"px newPosX="+newPosX+
        "px shift=-"+chunk+"px " +
        " note["+noteNum+"]="+noteName+" ch="+chNum+" measure="+measure;
        stave.appendChild(txt);
      }
    });
  }

  //Checking for duplicate arrNotes.
  arrNotes = document.getElementById("svg_body_part2").childNodes;

  for(let i=0; i < arrNotes.length; i++){
    if("rect" == arrNotes[i].tagName) posX = arrNotes[i].getAttribute("x");
    if("circle" == arrNotes[i].tagName) posX = arrNotes[i].getAttribute("cx");
    let ch = arrNotes[i].getAttribute("ch");
    let noteNum = arrNotes[i].getAttribute("note");

    await sleep2(10, i, arrNotes.length);

    Array.from(arrNotes).forEach((_note, idx) => {
      let _ch = _note.getAttribute("ch");
      let _noteNum = _note.getAttribute("note");
      if("rect" == _note.tagName) _posX = _note.getAttribute("x");
      if("circle" == _note.tagName) _posX = _note.getAttribute("cx");

      if(ch ==_ch && noteNum == _noteNum && posX == _posX && i !== idx){
        if(false == arrIndex.includes(i) && false == arrIndex.includes(idx)){
          dublicates.push(_note);
          arrIndex.push(i);
          arrIndex.push(idx);
          document.getElementById("svg_body_part2").removeChild(_note);
          changedData = true;
        }
      }
    });
  }
  if(0 < dublicates.length){
    dublicates.forEach((item, index) => {
      if("rect" == item.tagName) posX = item.getAttribute("x");
      if("circle" == item.tagName) posX = item.getAttribute("cx");
      for(let i=0; i < arrMeasures.length; i++){
        if(arrMeasures[i+1]){
          if(arrMeasures[i].tick <= posX*12 && arrMeasures[i+1].tick > posX*12){
            measure = arrMeasures[i].measure+1;
            break;
          }
        }else{
          if(arrMeasures[i].tick <= posX*12){
            measure = arrMeasures[i].measure+1;
            break;
          }
        }
      }
      txt = document.createElement("p");
      txt.setAttribute("style", "font-size:12px;height:16px;margin:0;");
      let noteNum = parseInt(item.getAttribute("note"));
      let noteName = synth.arrNotes[noteNum];
      let chNum = parseInt(item.getAttribute("ch"))+1;
      txt.innerHTML = "Duplicate removed: note["+noteNum+"]="+noteName+" channel="+chNum+" measure="+measure;
      stave.appendChild(txt);
    });
  }else{
    txt = document.createElement("p");
    txt.setAttribute("style", "font-size:12px;height:16px;margin:0;");
    txt.innerHTML = "No errors were found in the midi file.";
    stave.appendChild(txt);
  }
  if(true == changedData){
    var data = await saveTempMidi();
    await synth.loadMIDIUrl(data);
    changedData = false;
  }
  //progressVerify off
  let progressVerify = document.getElementById("progress_main");
  document.getElementById("sequencer_subfooter2").removeChild(progressVerify);
}
/**
 * [svgBodyContextMenu description]
 * @param  {[type]} e [description]
 * @return {[type]}   [description]
 */
function svgBodyContextMenu(e){
  e.preventDefault();
  insertNote(e);
}
/**
 * [svgBodyMouseDown description]
 * @param  {[type]} e [description]
 * @return {[type]}   [description]
 */
function svgBodyMouseDown(e){
  if(1 !== e.which)return;
  if("" == selTrackNum)return;

  if(svgBody.querySelector('#groupBorder')) svgBody.removeChild(groupBorder);

  if(!svgBody.querySelector('#groupBorder')){
    groupBorder = document.createElementNS(svgns,'rect');
    groupBorder.id = "groupBorder";

    groupBorderDrawing = true;
    groupBorderStartX = e.offsetX;
    groupBorderStartY = e.offsetY;
    groupBorder.setAttribute('x', groupBorderStartX);
    groupBorder.setAttribute('y', groupBorderStartY);
    groupBorder.setAttribute('width', 0);
    groupBorder.setAttribute('height', 0);

    groupBorder.removeEventListener("mousedown", groupBorderMouseDown);
    groupBorder.addEventListener("mousedown", groupBorderMouseDown);

    groupBorder.removeEventListener("mousemove",groupBorderMouseMove);
    groupBorder.addEventListener("mousemove",groupBorderMouseMove);

    groupBorder.removeEventListener("mouseup", groupBorderMouseUp);
    groupBorder.addEventListener("mouseup", groupBorderMouseUp);

    svgBody.appendChild(groupBorder);
  }
}
/**
 * [svgBodyMouseMove description]
 * @param  {[type]} e [description]
 * @return {[type]}   [description]
 */
function svgBodyMouseMove(e){
  if (true == groupBorderDrawing){
    groupBorderCorner = "left";
    let width = e.offsetX - groupBorderStartX;
    let height = e.offsetY - groupBorderStartY;

    if (width < 0) {
      groupBorder.setAttribute('x', e.offsetX);
      width = Math.abs(width);
      groupBorderCorner = "right";
    }
    if (height < 0) {
      groupBorder.setAttribute('y', e.offsetY);
      height = Math.abs(height);
      groupBorderCorner = "right";
    }

    groupBorder.setAttribute('width', width);
    groupBorder.setAttribute('height', height);
  }
}
/**
 * [svgBodyMouseUp description]
 * @param  {[type]} e [description]
 * @return {[type]}   [description]
 */
function svgBodyMouseUp(e){
  if (2 == e.button)return;
  if (!groupBorder)return;

  groupBorderDrawing = false;

  let x = parseInt(groupBorder.getAttribute("x"));
  let y = parseInt(groupBorder.getAttribute("y"));
  let w = parseInt(groupBorder.getAttribute("width"));
  let h = parseInt(groupBorder.getAttribute("height"));

  selectNotes(x,y,w,h);
}
/**
 * [svgBodyMouseLeave description]
 * @param  {[type]} e [description]
 * @return {[type]}   [description]
 */
function svgBodyMouseLeave(e){

  groupBorderDrawing = false;
}
/**
 * Select Notes from groupBorder
 *
 * @param  {[type]} x Pos X  border
 * @param  {[type]} y Pos Y  border
 * @param  {[type]} w Width  border
 * @param  {[type]} h Height border
 * @return {[type]}   [description]
 */
function selectNotes(x,y,w,h){
  let allNotes = document.querySelector("#svg_body_part2");
  let selAllNotes = allNotes.querySelectorAll(`[trk='${selTrackNum}']`);
  let posX;
  let posY;

  selNotesCash = [];
  let selNoteStorage = [];

  selAllNotes.forEach(function(note, idx){
    if (9 == curCh){
      posX = note.attributes.cx.value;
      posY = note.attributes.cy.value;
    }else{
      posX = note.attributes.x.value;
      posY = note.attributes.y.value;
    }

    if(x <= posX && x+w >= posX && y <= posY && y+h >= posY){
      note.attributes.fill.value = "blue";
      selNotesCash.push(note);
      selNoteStorage.push(note.outerHTML);
    }else{
      note.attributes.fill.value = "#0073AA";
    }
  });

  if(0 !== selNoteStorage.length) {
    //Save selNoteStorage to localStorage
    localStorage.removeItem("selNoteStorage");
    localStorage.setItem("selNoteStorage",JSON.stringify(selNoteStorage));
  }
}
function groupBorderMouseDown(e){
  let ppqn = synth.getPPQN();
  let svgBodyCoords = getCoords(svgBody);
  let groupBorderCoords = getCoords(groupBorder);
  let groupBorderShiftX = (e.clientX - groupBorderCoords.left);
  let groupBorderShiftY = (e.clientY - groupBorderCoords.top);
  let groupBorderLeftLast = (e.clientX - svgBodyCoords.left - groupBorderShiftX);
  let groupBorderTopLast = (e.clientY - svgBodyCoords.top - groupBorderShiftY);

  function moveAt(e){
    let groupBorderLeft = (e.clientX - svgBodyCoords.left - groupBorderShiftX);
    let groupBorderTop  = (e.clientY - svgBodyCoords.top - groupBorderShiftY);
    groupBorder.attributes.x.value = groupBorderLeft;
    groupBorder.attributes.y.value = groupBorderTop;
    groupBorder.attributes.width.value = groupBorderCoords.width;
    groupBorder.attributes.height.value = groupBorderCoords.height;

    let deltaX = groupBorderLeft - groupBorderLeftLast;
    let deltaY = groupBorderTopLast - groupBorderTop;

    selNotesCash.forEach(function(note, idx){
      if (9 == curCh){
        let cx = parseInt(note.attributes.cx.value);
        cx += deltaX;
        let cy = parseInt(note.attributes.cy.value);
        cy -= deltaY;

        note.attributes.cx.value = cx;
        note.attributes.cy.value = cy;
      }else{
        let x = parseInt(note.attributes.x.value);
        x += deltaX;
        let y = parseInt(note.attributes.y.value);
        y -= deltaY;

        note.attributes.x.value = x;
        note.attributes.y.value = y;
      }
    });
    groupBorderLeftLast = groupBorderLeft;
    groupBorderTopLast = groupBorderTop;
  }
  //Snapping notes to sequencer grid nodes
  function selNotesCashUpdate(){
    selNotesCash.forEach(function(note, idx){
      //along the X axis
      if(9 == curCh){
        let notePosX = parseInt(note.attributes.cx.value);
        let chunk = notePosX%(Math.round(ppqn/4/12));
        note.attributes.cx.value = notePosX - chunk;
      }else{
        let notePosX = parseInt(note.attributes.x.value);
        let chunk = notePosX%(Math.round(ppqn/4/12));
        note.attributes.x.value = notePosX - chunk;
      }
      //along the Y axis
      let notePosY;
      if(9 == curCh){
        notePosY = parseInt(note.attributes.cy.value);
      }else{
        notePosY = parseInt(note.attributes.y.value);
      }
      for(let i=1; i<arrNotesTrack.length; i++){
        if(notePosY >= arrNotesTrack[i] && notePosY < arrNotesTrack[i+1]){
          if(9 == curCh){
            let curPosY = arrNotesTrack.length - i;
            note.attributes.cy.value = arrNotesTrack[i]+4;
            note.attributes.note.value = curPosY + 20;
            break;
          }else{
            let curPosY = arrNotesTrack.length - i;
            note.attributes.y.value = arrNotesTrack[i];
            note.attributes.note.value = curPosY + 20;
            break;
          }
        }
      }
    });
    changedData = true;
  }
  document.onmousemove = function(e){
    if(groupBorder) moveAt(e);
  }
  document.onmouseup = function(e){
    document.onmousemove = null;
    document.onmouseup = null;

    groupBorder.onmousedown = null;
    groupBorder.onmousemove = null;
    groupBorder.onmouseup = null;

    selNotesCashUpdate();
  }
}
function groupBorderMouseMove(e){
  let mouseX = e.offsetX;
  let posX = parseInt(groupBorder.attributes.x.value);
  let groupBorderWidth = parseInt(groupBorder.attributes.width.value);

  if (posX < mouseX && mouseX < posX + groupBorderWidth){
    groupBorder.style.cursor = "move";
  }
}
function groupBorderMouseUp(e){
  if("left" == groupBorderCorner){
    if(svgBody.querySelector('#groupBorder')) svgBody.removeChild(groupBorder);
  }else{
    groupBorderCorner = "left";
  }
}
