diff --git a/js/blocks/WidgetBlocks.js b/js/blocks/WidgetBlocks.js index a1673ba059..3b0db17dbc 100644 --- a/js/blocks/WidgetBlocks.js +++ b/js/blocks/WidgetBlocks.js @@ -1,3 +1,54 @@ +/* + global FlowBlock, _, last, DEFAULTFILTERTYPE, FILTERTYPES, instrumentsFilters, StackClampBlock, + DEFAULTMODE, TemperamentWidget, TimbreWidget, DEFAULTVOICE, NOINPUTERRORMSG, instrumentsEffects, + MeterWidget, Oscilloscope, turtles, ModeWidget, Tempo, PitchDrumMatrix, PitchSlider, + beginnerMode, MusicKeyboard, PitchStaircase, RhythmRuler, PhraseMaker, StatusMatrix + */ + +/* + Global locations + js/protoblocks.js + FlowBlock, StackClampBlock + js/utils/utils.js + _, last + js/utils/musicutils.js + DEFAULTFILTERTYPE, FILTERTYPES, DEFAULTMODE, DEFAULTVOICE + js/utils/synthutils.js + instrumentsFilters, instrumentsEffects + js/logo.js + NOINPUTERRORMSG + js/activity.js + turtles, beginnerMode + js/widgets/temperament.js + TemperamentWidget + js/widgets/timbre.js + TimbreWidget + js/widgets/meterwidget.js + MeterWidget + js/widgets/oscilloscope.js + Oscilloscope + js/widgets/modewidget.js + ModeWidget + js/widgets/tempo.js + Tempo + js/widgets/pitchdrummatrix.js + PitchDrumMatrix + js/widgets/pitchslider.js + PitchSlider + js/widgets/musickeyboard.js + MusicKeyboard + js/widgets/pitchstaircase.js + PitchStairCase + js/widgets/rhythmruler.js + RhythmRuler + js/widgets/phrasemaker.js + PhraseMaker + js/widgets/status.js + StatusMatrix + */ + +/* exported setupWidgetBlocks */ + function setupWidgetBlocks() { class EnvelopeBlock extends FlowBlock { constructor() { @@ -209,7 +260,7 @@ function setupWidgetBlocks() { const listenerName = "_temperament_" + turtle; logo.setDispatchBlock(blk, turtle, listenerName); - const __listener = function(event) { + const __listener = () => { logo.temperament.init(); }; @@ -325,7 +376,7 @@ function setupWidgetBlocks() { const listenerName = "_timbre_" + turtle; logo.setDispatchBlock(blk, turtle, listenerName); - const __listener = function(event) { + const __listener = () => { logo.timbre.init(); }; @@ -366,7 +417,7 @@ function setupWidgetBlocks() { const listenerName = "_meterwidget_" + turtle; logo.setDispatchBlock(blk, turtle, listenerName); - const __listener = function(event) { + const __listener = () => { logo.meterWidget = new MeterWidget(blk); logo.insideMeterWidget = false; @@ -419,7 +470,7 @@ function setupWidgetBlocks() { const listenerName = "_oscilloscope_" + turtle; logo.setDispatchBlock(blk, turtle, listenerName); - const __listener = function(event) { + const __listener = () => { logo.Oscilloscope = new Oscilloscope(logo); logo.inOscilloscope = false; }; @@ -461,7 +512,7 @@ function setupWidgetBlocks() { const listenerName = "_modewidget_" + turtle; logo.setDispatchBlock(blk, turtle, listenerName); - const __listener = function(event) { + const __listener = () => { logo.modeWidget = new ModeWidget(); logo.insideModeWidget = false; }; @@ -499,7 +550,7 @@ function setupWidgetBlocks() { ]); } - flow(args, logo, turtle, blk, receivedArg, actionArgs, isflow) { + flow(args, logo, turtle, blk) { if (logo.tempo === null) { logo.tempo = new Tempo(); } @@ -511,7 +562,7 @@ function setupWidgetBlocks() { const listenerName = "_tempo_" + turtle; logo.setDispatchBlock(blk, turtle, listenerName); - const __listener = function(event) { + const __listener = () => { logo.tempo.init(); }; @@ -569,7 +620,7 @@ function setupWidgetBlocks() { const listenerName = "_pitchdrummatrix_" + turtle; logo.setDispatchBlock(blk, turtle, listenerName); - const __listener = function(event) { + const __listener = () => { if ( logo.pitchDrumMatrix.drums.length === 0 || logo.pitchDrumMatrix.rowLabels.length === 0 @@ -626,7 +677,7 @@ function setupWidgetBlocks() { const listenerName = "_pitchslider_" + turtle; logo.setDispatchBlock(blk, turtle, listenerName); - const __listener = function(event) { + const __listener = () => { logo.pitchSlider.init(logo); logo.inPitchSlider = false; }; @@ -670,7 +721,8 @@ function setupWidgetBlocks() { if (this.lang !== "ja") this.hidden = true; } - flow(args, logo, turtle, blk, receivedArg, actionArgs, isflow) {} + flow() {} + // flow(args, logo, turtle, blk, receivedArg, actionArgs, isflow) {} } class MusicKeyboard2Block extends StackClampBlock { @@ -758,8 +810,8 @@ function setupWidgetBlocks() { const listenerName = "_musickeyboard_" + turtle; logo.setDispatchBlock(blk, turtle, listenerName); - const __listener = function(event) { - logo.musicKeyboard.init(logo); + const __listener = () => { + logo.musicKeyboard.init(); }; logo.setTurtleListener(turtle, listenerName, __listener); @@ -807,7 +859,7 @@ function setupWidgetBlocks() { const listenerName = "_pitchstaircase_" + turtle; logo.setDispatchBlock(blk, turtle, listenerName); - const __listener = function(event) { + const __listener = () => { logo.pitchStaircase.init(logo); logo.inPitchStaircase = false; }; @@ -894,7 +946,7 @@ function setupWidgetBlocks() { const listenerName = "_rhythmruler_" + turtle; logo.setDispatchBlock(blk, turtle, listenerName); - const __listener = function(event) { + const __listener = () => { logo.rhythmRuler.init(); }; @@ -1037,7 +1089,7 @@ function setupWidgetBlocks() { const listenerName = "_matrix_" + turtle; logo.setDispatchBlock(blk, turtle, listenerName); - const __listener = function(event) { + const __listener = () => { if ( logo.tupletRhythms.length === 0 || logo.phraseMaker.rowLabels.length === 0 @@ -1143,7 +1195,7 @@ function setupWidgetBlocks() { const listenerName = "_status_" + turtle; logo.setDispatchBlock(blk, turtle, listenerName); - const __listener = function(event) { + const __listener = () => { logo.statusMatrix.init(logo); logo.inStatusMatrix = false; }; diff --git a/js/widgets/musickeyboard.js b/js/widgets/musickeyboard.js index 66f3011b48..54600b0178 100644 --- a/js/widgets/musickeyboard.js +++ b/js/widgets/musickeyboard.js @@ -1,61 +1,254 @@ -// Copyright (c) 2015 Jefferson Lee -// Copyright (c) 2018 Ritwik Abhishek -// Copyright (c) 2018,20 Walter Bender -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the The GNU Affero General Public -// License as published by the Free Software Foundation; either -// version 3 of the License, or (at your option) any later version. -// -// You should have received a copy of the GNU Affero General Public -// License along with this library; if not, write to the Free Software -// Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA - -function MusicKeyboard() { - const FAKEBLOCKNUMBER = 100000; - const BUTTONDIVWIDTH = 535; // 5 buttons - const OUTERWINDOWWIDTH = 758; - const INNERWINDOWWIDTH = 50; - const BUTTONSIZE = 53; - const ICONSIZE = 32; +/** + * @file This contains the prototype of the MusicKeyboard Widget. + * + * @copyright 2015 Jefferson Lee + * @copyright 2018 Ritwik Abhishek + * @copyright 2018,20 Walter Bender + * + * @license + * This program is free software; you can redistribute it and/or modify it under the terms of the + * The GNU Affero General Public License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * You should have received a copy of the GNU Affero General Public License along with this + * library; if not, write to the Free Software Foundation, 51 Franklin Street, Suite 500 Boston, + * MA 02110-1335 USA. + */ + +/* + global platformColor, docById, wheelnav, slicePath, logo, Singer, last, _, FIXEDSOLFEGE1 + SHARP, FLAT, noteToFrequency, EIGHTHNOTEWIDTH, MATRIXSOLFEHEIGHT, MATRIXSOLFEWIDTH, + i18nSolfege, toFraction, blocks, getNote, PREVIEWVOLUME, DEFAULTVOICE, PITCHES3, + SOLFEGENAMES, PITCHES2, PITCHES, NOTESSHARP, NOTESFLAT, convertFromSolfege, SOLFEGECONVERSIONTABLE + + + */ + +/* exported MusicKeyboard */ + +/** + * @class + * @classdesc pertains to setting up all features of the musickeyboard widget and its UI features. + * + * Private members' names begin with underscore '_". + */ +class MusicKeyboard { + static FAKEBLOCKNUMBER = 100000; + static BUTTONDIVWIDTH = 535; // 5 buttons + static OUTERWINDOWWIDTH = 758; + static INNERWINDOWWIDTH = 50; + static BUTTONSIZE = 53; + static ICONSIZE = 32; // Mapping between keycodes and virtual keyboard - const BLACKKEYS = [87, 69, 82, 84, 89, 85, 73, 79]; - const WHITEKEYS = [65, 83, 68, 70, 71, 72, 74, 75, 76]; - const SPACE = 32; + static BLACKKEYS = [87, 69, 82, 84, 89, 85, 73, 79]; + static WHITEKEYS = [65, 83, 68, 70, 71, 72, 74, 75, 76]; + static SPACE = 32; - const saveOnKeyDown = document.onkeydown; - const saveOnKeyUp = document.onkeyup; + static saveOnKeyDown = document.onkeydown; + static saveOnKeyUp = document.onkeyup; + static beginnerMode = localStorage.beginnerMode; + static unit = MusicKeyboard.beginnerMode === "true" ? 8 : 16; + static selectedNotes = []; - const w = window.innerWidth; - this._cellScale = w / 1200; + /** + * @constructor + */ + + constructor() { + const w = window.innerWidth; + this._cellScale = w / 1200; + + this._stopOrCloseClicked = false; + this.playingNow = false; + + this.instruments = []; + this.noteNames = []; + this.octaves = []; + this.keyboardShown = true; + this.layout = []; + this.idContainer = []; + this.tick = false; + this.meterArgs = [4, 1 / 4]; + + // Map between keyboard element ids and the note associated with the key. + this.noteMapper = {}; + this.blockNumberMapper = {}; + this.instrumentMapper = {}; + + this._rowBlocks = []; + + // Each element in the array is [start time, note, id, duration, voice]. + this._notesPlayed = []; + } + + /** + * Initialises the MusicKeyboard widget. + * @returns {void} + */ + + init() { + this.tick = false; + this.playingNow = false; + let w = window.innerWidth; + this._cellScale = w / 1200; + + const widgetWindow = window.widgetWindows.windowFor(this, "music keyboard"); + this.widgetWindow = widgetWindow; + widgetWindow.clear(); + widgetWindow.show(); + this._keysLayout(); + const tur = logo.turtles.ithTurtle(0); + this.bpm = tur.singer.bpm.length > 0 ? last(tur.singer.bpm) : Singer.masterBPM; + + widgetWindow.onclose = () => { + let myNode; + document.onkeydown = MusicKeyboard.saveOnKeyDown; + document.onkeyup = MusicKeyboard.saveOnKeyUp; + + if (document.getElementById("keyboardHolder2")) { + document.getElementById("keyboardHolder2").style.display = "none"; + } + + myNode = document.getElementById("myrow"); + if (myNode != null) { + myNode.innerHTML = ""; + } + + myNode = document.getElementById("myrow2"); + if (myNode != null) { + myNode.innerHTML = ""; + } + + MusicKeyboard.selectedNotes = []; + if (this.loopTick) this.loopTick.stop(); + widgetWindow.destroy(); + }; + + this.playButton = widgetWindow.addButton( + "play-button.svg", + MusicKeyboard.ICONSIZE, + _("Play") + ); + + this.playButton.onclick = () => { + logo.turtleDelay = 0; + this.processSelected(); + this.playAll(); + }; + + widgetWindow.addButton( + "export-chunk.svg", + MusicKeyboard.ICONSIZE, + _("Save") + ).onclick = () => { + this._save(); + }; + + widgetWindow.addButton( + "erase-button.svg", + MusicKeyboard.ICONSIZE, + _("Clear") + ).onclick = () => { + this._notesPlayed = []; + MusicKeyboard.selectedNotes = []; + // if (!that.keyboardShown) { + this._createTable(); + // } + }; + + widgetWindow.addButton("add2.svg", MusicKeyboard.ICONSIZE, _("Add note")).onclick = () => { + this._createAddRowPieSubmenu(); + }; + + this.midiButton = widgetWindow.addButton("midi.svg", MusicKeyboard.ICONSIZE, _("MIDI")); + this.midiButton.onclick = () => { + this.doMIDI(); + }; + + this.tickButton = widgetWindow.addButton( + "metronome.svg", + MusicKeyboard.ICONSIZE, + _("Metronome") + ); + this.tickButton.onclick = () => { + if (this.tick) { + this.tick = false; + this.loopTick.stop(); + } else { + this.tick = true; + logo.synth.loadSynth(0, "cow bell"); + this.loopTick = logo.synth.loop( + 0, + "cow bell", + "C5", + 1 / 64, + 0, + this.bpm || 90, + 0.07 + ); + setTimeout(() => { + logo.synth.start(); + }, 500); + } + }; + + // Append keyboard and div on widget windows + this.keyboardDiv = document.createElement("div"); + const attr = document.createAttribute("id"); + attr.value = "mkbKeyboardDiv"; + this.keyboardDiv.setAttributeNode(attr); + this.keyTable = document.createElement("div"); + widgetWindow.getWidgetBody().append(this.keyboardDiv); + widgetWindow.getWidgetBody().append(this.keyTable); + widgetWindow.getWidgetBody().style.height = "550px"; + widgetWindow.getWidgetBody().style.width = "1000px"; - const beginnerMode = localStorage.beginnerMode; + this._createKeyboard(); - const unit = beginnerMode === "true" ? 8 : 16; - this._stopOrCloseClicked = false; - this.playingNow = false; + this._createTable(); - this.instruments = []; - this.noteNames = []; - this.octaves = []; - this.keyboardShown = true; - this.layout = []; - this.idContainer = []; - this.tick = false; - this.meterArgs = [4, 1 / 4]; + w = Math.max( + Math.min(window.innerWidth, this._cellScale * MusicKeyboard.OUTERWINDOWWIDTH - 20), + MusicKeyboard.BUTTONDIVWIDTH + ); - // Map between keyboard element ids and the note associated with the key. - this.noteMapper = {}; - this.blockNumberMapper = {}; - this.instrumentMapper = {}; - let selectedNotes = []; + //Change widget size on fullscreen mode, else + //revert back to original size on unfullscreen mode + widgetWindow.onmaximize = () => { + if (widgetWindow._maximized) { + widgetWindow.getWidgetBody().style.position = "absolute"; + widgetWindow.getWidgetBody().style.height = "calc(100vh - 64px)"; + widgetWindow.getWidgetBody().style.width = "200vh"; + docById("mkbOuterDiv").style.width = "calc(200vh - 64px)"; + docById("keyboardHolder2").style.width = "calc(200vh - 64px)"; + try { + docById("mkbInnerDiv").style.width = "calc(200vh - 64px)"; + } catch (e) { + // Does this happen? + // console.debug("Error calculating InnerDiv width"); + } + + widgetWindow.getWidgetBody().style.left = "70px"; + } else { + widgetWindow.getWidgetBody().style.position = "relative"; + widgetWindow.getWidgetBody().style.left = "0px"; + widgetWindow.getWidgetBody().style.height = "550px"; + widgetWindow.getWidgetBody().style.width = "1000px"; + docById("mkbOuterDiv").style.width = w + "px"; + } + }; + + widgetWindow.sendToCenter(); + } - this._rowBlocks = []; + /** + * @public + * @param {number} rowBlock + * @returns {void} + */ - // Each element in the array is [start time, note, id, duration, voice]. - this._notesPlayed = []; - this.addRowBlock = (rowBlock) => { + addRowBlock(rowBlock) { // In case there is a repeat block, use a unique block number // for each instance. while (this._rowBlocks.indexOf(rowBlock) !== -1) { @@ -63,11 +256,15 @@ function MusicKeyboard() { } this._rowBlocks.push(rowBlock); - }; + } - this.processSelected = () => { + /** + * @public + * @returns {void} + */ + processSelected() { if (this._notesPlayed.length === 0) { - selectedNotes = []; + MusicKeyboard.selectedNotes = []; return; } @@ -79,7 +276,7 @@ function MusicKeyboard() { // selectedNotes is used for playback. Coincident notes are // grouped together. It is built from notesPlayed. - selectedNotes = [ + MusicKeyboard.selectedNotes = [ { noteOctave: [this._notesPlayed[0].noteOctave], objId: [this._notesPlayed[0].objId], @@ -96,17 +293,17 @@ function MusicKeyboard() { i < this._notesPlayed.length && this._notesPlayed[i].startTime === this._notesPlayed[i - 1].startTime ) { - selectedNotes[j].noteOctave.push(this._notesPlayed[i].noteOctave); - selectedNotes[j].objId.push(this._notesPlayed[i].objId); - selectedNotes[j].duration.push(this._notesPlayed[i].duration); - selectedNotes[j].voice.push(this._notesPlayed[i].voice); - selectedNotes[j].blockNumber.push(this._notesPlayed[i].blockNumber); + MusicKeyboard.selectedNotes[j].noteOctave.push(this._notesPlayed[i].noteOctave); + MusicKeyboard.selectedNotes[j].objId.push(this._notesPlayed[i].objId); + MusicKeyboard.selectedNotes[j].duration.push(this._notesPlayed[i].duration); + MusicKeyboard.selectedNotes[j].voice.push(this._notesPlayed[i].voice); + MusicKeyboard.selectedNotes[j].blockNumber.push(this._notesPlayed[i].blockNumber); i++; } j++; if (i < this._notesPlayed.length) { - selectedNotes.push({ + MusicKeyboard.selectedNotes.push({ noteOctave: [this._notesPlayed[i].noteOctave], objId: [this._notesPlayed[i].objId], duration: [this._notesPlayed[i].duration], @@ -116,10 +313,14 @@ function MusicKeyboard() { }); } } - }; + } + + /** + * @public + * @returns {void} + */ - this.addKeyboardShortcuts = function () { - // ; + addKeyboardShortcuts() { let duration = 0; const startTime = {}; const temp1 = {}; @@ -127,20 +328,20 @@ function MusicKeyboard() { const current = new Set(); const __startNote = (event) => { - let i, id, ele; - if (WHITEKEYS.indexOf(event.keyCode) !== -1) { - i = WHITEKEYS.indexOf(event.keyCode); + let i, id; + if (MusicKeyboard.WHITEKEYS.indexOf(event.keyCode) !== -1) { + i = MusicKeyboard.WHITEKEYS.indexOf(event.keyCode); id = "whiteRow" + i.toString(); - } else if (BLACKKEYS.indexOf(event.keyCode) !== -1) { - i = BLACKKEYS.indexOf(event.keyCode); + } else if (MusicKeyboard.BLACKKEYS.indexOf(event.keyCode) !== -1) { + i = MusicKeyboard.BLACKKEYS.indexOf(event.keyCode); id = "blackRow" + i.toString(); - } else if (SPACE == event.keyCode) { + } else if (MusicKeyboard.SPACE == event.keyCode) { id = "rest"; } - ele = docById(id); + const ele = docById(id); if (!(id in startTime)) { - startDate = new Date(); + const startDate = new Date(); startTime[id] = startDate.getTime(); } @@ -164,16 +365,20 @@ function MusicKeyboard() { return; } - this._logo.synth.trigger(0, temp2[id], 1, this.instrumentMapper[id], null, null); + logo.synth.trigger(0, temp2[id], 1, this.instrumentMapper[id], null, null); if (this.tick) { - restDuration = (startTime[id] - this.endTime) / 1000.0; + let restDuration = (startTime[id] - this.endTime) / 1000.0; restDuration /= 60; // time in minutes restDuration *= this.bpm; restDuration *= this.meterArgs[1]; - restDuration = parseFloat((Math.round(restDuration * unit) / unit).toFixed(4)); + restDuration = parseFloat( + ( + Math.round(restDuration * MusicKeyboard.unit) / MusicKeyboard.unit + ).toFixed(4) + ); if (restDuration === 0) { restDuration = 0; @@ -191,7 +396,7 @@ function MusicKeyboard() { } }; - const __keyboarddown = function (event) { + const __keyboarddown = (event) => { if (current.has(event.keyCode)) return; __startNote(event); @@ -201,19 +406,19 @@ function MusicKeyboard() { }; const __endNote = (event) => { - let i, id, ele, no; - if (WHITEKEYS.indexOf(event.keyCode) !== -1) { - i = WHITEKEYS.indexOf(event.keyCode); + let i, id; + if (MusicKeyboard.WHITEKEYS.indexOf(event.keyCode) !== -1) { + i = MusicKeyboard.WHITEKEYS.indexOf(event.keyCode); id = "whiteRow" + i.toString(); - } else if (BLACKKEYS.indexOf(event.keyCode) !== -1) { - i = BLACKKEYS.indexOf(event.keyCode); + } else if (MusicKeyboard.BLACKKEYS.indexOf(event.keyCode) !== -1) { + i = MusicKeyboard.BLACKKEYS.indexOf(event.keyCode); id = "blackRow" + i.toString(); - } else if (SPACE == event.keyCode) { + } else if (MusicKeyboard.SPACE == event.keyCode) { id = "rest"; } - ele = docById(id); - newDate = new Date(); + const ele = docById(id); + const newDate = new Date(); this.endTime = newDate.getTime(); duration = (this.endTime - startTime[id]) / 1000.0; @@ -224,16 +429,16 @@ function MusicKeyboard() { ele.style.backgroundColor = "white"; } - no = ele.getAttribute("alt").split("__")[2]; - duration /= 60; duration *= this.bpm; duration *= this.meterArgs[1]; - duration = parseFloat((Math.round(duration * unit) / unit).toFixed(4)); + duration = parseFloat( + (Math.round(duration * MusicKeyboard.unit) / MusicKeyboard.unit).toFixed(4) + ); if (duration === 0) { - duration = 1 / unit; + duration = 1 / MusicKeyboard.unit; } else if (duration < 0) { duration = -duration; } @@ -245,7 +450,7 @@ function MusicKeyboard() { duration: parseFloat(duration) }); } else { - this._logo.synth.stopSound(0, this.instrumentMapper[id], temp2[id]); + logo.synth.stopSound(0, this.instrumentMapper[id], temp2[id]); this._notesPlayed.push({ startTime: startTime[id], noteOctave: temp2[id], @@ -263,7 +468,7 @@ function MusicKeyboard() { } }; - const __keyboardup = function (event) { + const __keyboardup = (event) => { current.delete(event.keyCode); __endNote(event); //event.preventDefault(); @@ -271,9 +476,17 @@ function MusicKeyboard() { document.onkeydown = __keyboarddown; document.onkeyup = __keyboardup; - }; + } + + /** + * @public + * @param {HTMLElement} element + * @param {number} i + * @param {number} blockNumber + * @returns {void} + */ - this.loadHandler = function (element, i, blockNumber) { + loadHandler(element, i, blockNumber) { const temp1 = this.layout[i].noteName; let temp2; if (temp1 === "hertz") { @@ -298,7 +511,7 @@ function MusicKeyboard() { startDate = new Date(); startTime = startDate.getTime(); // Milliseconds(); element.style.backgroundColor = platformColor.orange; - this._logo.synth.trigger( + logo.synth.trigger( 0, this.noteMapper[element.id], 1, @@ -308,8 +521,8 @@ function MusicKeyboard() { ); }; - element.onmousedown = function () { - __startNote(this); + element.onmousedown = () => { + __startNote(element); }; const __endNote = (element) => { @@ -323,12 +536,12 @@ function MusicKeyboard() { const now = new Date(); duration = now.getTime() - startTime; duration /= 1000; - this._logo.synth.stopSound( + logo.synth.stopSound( 0, this.instrumentMapper[element.id], this.noteMapper[element.id] ); - if (beginnerMode === "true") { + if (MusicKeyboard.beginnerMode === "true") { duration = parseFloat((Math.round(duration * 8) / 8).toFixed(3)); } else { duration = parseFloat((Math.round(duration * 16) / 16).toFixed(4)); @@ -351,159 +564,22 @@ function MusicKeyboard() { this._createTable(); }; - element.onmouseout = function () { + element.onmouseout = () => { // __endNote(); }; - element.onmouseup = function () { - __endNote(this); - }; - }; - - this.init = function (logo) { - this._logo = logo; - this.tick = false; - this.playingNow = false; - let w = window.innerWidth; - this._cellScale = w / 1200; - const iconSize = ICONSIZE * this._cellScale; - - const widgetWindow = window.widgetWindows.windowFor(this, "music keyboard"); - this.widgetWindow = widgetWindow; - widgetWindow.clear(); - widgetWindow.show(); - this._keysLayout(); - const tur = logo.turtles.ithTurtle(0); - this.bpm = tur.singer.bpm.length > 0 ? last(tur.singer.bpm) : Singer.masterBPM; - - widgetWindow.onclose = () => { - let myNode; - document.onkeydown = saveOnKeyDown; - document.onkeyup = saveOnKeyUp; - - if (document.getElementById("keyboardHolder2")) { - document.getElementById("keyboardHolder2").style.display = "none"; - } - - myNode = document.getElementById("myrow"); - if (myNode != null) { - myNode.innerHTML = ""; - } - - myNode = document.getElementById("myrow2"); - if (myNode != null) { - myNode.innerHTML = ""; - } - - selected = []; - selectedNotes = []; - if (this.loopTick) this.loopTick.stop(); - widgetWindow.destroy(); - }; - - this.playButton = widgetWindow.addButton("play-button.svg", ICONSIZE, _("Play")); - - this.playButton.onclick = () => { - this._logo.turtleDelay = 0; - this.processSelected(); - this.playAll(); - }; - - widgetWindow.addButton("export-chunk.svg", ICONSIZE, _("Save")).onclick = () => { - this._save(); - }; - - widgetWindow.addButton("erase-button.svg", ICONSIZE, _("Clear")).onclick = () => { - this._notesPlayed = []; - selectedNotes = []; - // if (!that.keyboardShown) { - this._createTable(); - // } - }; - - widgetWindow.addButton("add2.svg", ICONSIZE, _("Add note")).onclick = () => { - this._createAddRowPieSubmenu(); - }; - - this.midiButton = widgetWindow.addButton("midi.svg", ICONSIZE, _("MIDI")); - this.midiButton.onclick = () => { - this.doMIDI(); - }; - - this.tickButton = widgetWindow.addButton("metronome.svg", ICONSIZE, _("Metronome")); - this.tickButton.onclick = () => { - if (this.tick) { - this.tick = false; - this.loopTick.stop(); - } else { - this.tick = true; - this._logo.synth.loadSynth(0, "cow bell"); - this.loopTick = this._logo.synth.loop( - 0, - "cow bell", - "C5", - 1 / 64, - 0, - this.bpm || 90, - 0.07 - ); - setTimeout(() => { - this._logo.synth.start(); - }, 500); - } - }; - - // Append keyboard and div on widget windows - this.keyboardDiv = document.createElement("div"); - const attr = document.createAttribute("id"); - attr.value = "mkbKeyboardDiv"; - this.keyboardDiv.setAttributeNode(attr); - this.keyTable = document.createElement("div"); - widgetWindow.getWidgetBody().append(this.keyboardDiv); - widgetWindow.getWidgetBody().append(this.keyTable); - widgetWindow.getWidgetBody().style.height = "550px"; - widgetWindow.getWidgetBody().style.width = "1000px"; - - this._createKeyboard(); - - this._createTable(); - - w = Math.max( - Math.min(window.innerWidth, this._cellScale * OUTERWINDOWWIDTH - 20), - BUTTONDIVWIDTH - ); - - //Change widget size on fullscreen mode, else - //revert back to original size on unfullscreen mode - widgetWindow.onmaximize = function () { - if (widgetWindow._maximized) { - widgetWindow.getWidgetBody().style.position = "absolute"; - widgetWindow.getWidgetBody().style.height = "calc(100vh - 64px)"; - widgetWindow.getWidgetBody().style.width = "200vh"; - docById("mkbOuterDiv").style.width = "calc(200vh - 64px)"; - docById("keyboardHolder2").style.width = "calc(200vh - 64px)"; - try { - docById("mkbInnerDiv").style.width = "calc(200vh - 64px)"; - } catch (e) { - // Does this happen? - console.debug("Error calculating InnerDiv width"); - } - - widgetWindow.getWidgetBody().style.left = "70px"; - } else { - widgetWindow.getWidgetBody().style.position = "relative"; - widgetWindow.getWidgetBody().style.left = "0px"; - widgetWindow.getWidgetBody().style.height = "550px"; - widgetWindow.getWidgetBody().style.width = "1000px"; - docById("mkbOuterDiv").style.width = w + "px"; - } + element.onmouseup = () => { + __endNote(element); }; + } - widgetWindow.sendToCenter(); - }; + /** + * @public + * @returns {void} + */ - this.playAll = function () { - if (selectedNotes.length <= 0) { + playAll() { + if (MusicKeyboard.selectedNotes.length <= 0) { return; } @@ -519,26 +595,26 @@ function MusicKeyboard() { '" alt="' + _("stop") + '" height="' + - ICONSIZE + + MusicKeyboard.ICONSIZE + '" width="' + - ICONSIZE + + MusicKeyboard.ICONSIZE + '" vertical-align="middle" align-content="center"> '; - if (selectedNotes.length < 1) { + if (MusicKeyboard.selectedNotes.length < 1) { return; } const notes = []; - let ele, zx, res, cell, maxWidth; - for (let i = 0; i < selectedNotes[0].noteOctave.length; i++) { - if (this.keyboardShown && selectedNotes[0].objId[0] !== null) { - ele = docById(selectedNotes[0].objId[i]); + let ele, zx, res, cell; + for (let i = 0; i < MusicKeyboard.selectedNotes[0].noteOctave.length; i++) { + if (this.keyboardShown && MusicKeyboard.selectedNotes[0].objId[0] !== null) { + ele = docById(MusicKeyboard.selectedNotes[0].objId[i]); if (ele !== null) { ele.style.backgroundColor = "lightgrey"; } } - zx = selectedNotes[0].noteOctave[i]; + zx = MusicKeyboard.selectedNotes[0].noteOctave[i]; res = zx; if (typeof zx === "string") { res = zx.replace(SHARP, "#").replace(FLAT, "b"); @@ -553,8 +629,12 @@ function MusicKeyboard() { } this._stopOrCloseClicked = false; - this._playChord(notes, selectedNotes[0].duration, selectedNotes[0].voice); - maxWidth = Math.max.apply(Math, selectedNotes[0].duration); + this._playChord( + notes, + MusicKeyboard.selectedNotes[0].duration, + MusicKeyboard.selectedNotes[0].voice + ); + const maxWidth = Math.max.apply(Math, MusicKeyboard.selectedNotes[0].duration); this.playOne(1, maxWidth, playButtonCell); } else { if (!this.keyboardShown) { @@ -572,17 +652,24 @@ function MusicKeyboard() { '" alt="' + _("Play") + '" height="' + - ICONSIZE + + MusicKeyboard.ICONSIZE + '" width="' + - ICONSIZE + + MusicKeyboard.ICONSIZE + '" vertical-align="middle" align-content="center"> '; } - }; + } - this.playOne = function (counter, time, playButtonCell) { + /** + * @public + * @param {number} counter + * @param {string} time + * @param {HTMLElement} playButtonCell + * @returns {void} + */ + playOne(counter, time, playButtonCell) { setTimeout(() => { - let cell, eleid, ele, notes, id, zx, res, maxWidth; - if (counter < selectedNotes.length) { + let cell, eleid, ele, notes, zx, res, maxWidth; + if (counter < MusicKeyboard.selectedNotes.length) { if (this._stopOrCloseClicked) { return; } @@ -592,9 +679,16 @@ function MusicKeyboard() { cell.style.backgroundColor = platformColor.selectorBackground; } - if (this.keyboardShown && selectedNotes[counter - 1].objId[0] !== null) { - for (let i = 0; i < selectedNotes[counter - 1].noteOctave.length; i++) { - eleid = selectedNotes[counter - 1].objId[i]; + if ( + this.keyboardShown && + MusicKeyboard.selectedNotes[counter - 1].objId[0] !== null + ) { + for ( + let i = 0; + i < MusicKeyboard.selectedNotes[counter - 1].noteOctave.length; + i++ + ) { + eleid = MusicKeyboard.selectedNotes[counter - 1].objId[i]; ele = docById(eleid); if (eleid.includes("blackRow")) { ele.style.backgroundColor = "black"; @@ -605,19 +699,18 @@ function MusicKeyboard() { } notes = []; - for (let i = 0; i < selectedNotes[counter].noteOctave.length; i++) { - if (this.keyboardShown && selectedNotes[counter].objId[0] !== null) { - id = this.idContainer.findIndex((ele) => { - return ele[1] === selectedNotes[counter].objId[i]; - }); - - ele = docById(selectedNotes[counter].objId[i]); + for (let i = 0; i < MusicKeyboard.selectedNotes[counter].noteOctave.length; i++) { + if ( + this.keyboardShown && + MusicKeyboard.selectedNotes[counter].objId[0] !== null + ) { + ele = docById(MusicKeyboard.selectedNotes[counter].objId[i]); if (ele !== null) { ele.style.backgroundColor = "lightgrey"; } } - zx = selectedNotes[counter].noteOctave[i]; + zx = MusicKeyboard.selectedNotes[counter].noteOctave[i]; res = zx; if (typeof zx === "string") { res = zx.replace(SHARP, "#").replace(FLAT, "b"); @@ -628,12 +721,12 @@ function MusicKeyboard() { if (this.playingNow) { this._playChord( notes, - selectedNotes[counter].duration, - selectedNotes[counter].voice + MusicKeyboard.selectedNotes[counter].duration, + MusicKeyboard.selectedNotes[counter].voice ); } - maxWidth = Math.max.apply(Math, selectedNotes[counter].duration); + maxWidth = Math.max.apply(Math, MusicKeyboard.selectedNotes[counter].duration); this.playOne(counter + 1, maxWidth, playButtonCell); } else { playButtonCell.innerHTML = @@ -644,9 +737,9 @@ function MusicKeyboard() { '" alt="' + _("Play") + '" height="' + - ICONSIZE + + MusicKeyboard.ICONSIZE + '" width="' + - ICONSIZE + + MusicKeyboard.ICONSIZE + '" vertical-align="middle" align-content="center"> '; this.playingNow = false; if (!this.keyboardShown) { @@ -656,37 +749,50 @@ function MusicKeyboard() { } } }, time * 1000 + 125); - }; + } - this._playChord = (notes, noteValue, instruments) => { + /** + * @private + * @param {Array} notes + * @param {Array} noteValue + * @param {Array} instruments + * @returns {void} + */ + + _playChord(notes, noteValue, instruments) { if (notes[0] === "R") { return; } setTimeout(() => { - this._logo.synth.trigger(0, notes[0], noteValue[0], instruments[0], null, null); + logo.synth.trigger(0, notes[0], noteValue[0], instruments[0], null, null); }, 1); if (notes.length > 1) { setTimeout(() => { - this._logo.synth.trigger(0, notes[1], noteValue[0], instruments[1], null, null); + logo.synth.trigger(0, notes[1], noteValue[0], instruments[1], null, null); }, 1); } if (notes.length > 2) { setTimeout(() => { - this._logo.synth.trigger(0, notes[2], noteValue[0], instruments[2], null, null); + logo.synth.trigger(0, notes[2], noteValue[0], instruments[2], null, null); }, 1); } if (notes.length > 3) { setTimeout(() => { - this._logo.synth.trigger(0, notes[3], noteValue[0], instruments[3], null, null); + logo.synth.trigger(0, notes[3], noteValue[0], instruments[3], null, null); }, 1); } - }; + } - this._keysLayout = function () { + /** + * @private + * @returns {void} + */ + + _keysLayout() { this.layout = []; const sortableList = []; for (let i = 0; i < this.noteNames.length; i++) { @@ -710,7 +816,7 @@ function MusicKeyboard() { sortableList.push({ frequency: noteToFrequency( this.noteNames[i] + this.octaves[i], - this._logo.turtles.ithTurtle(0).singer.keySignature + logo.turtles.ithTurtle(0).singer.keySignature ), noteName: this.noteNames[i], noteOctave: this.octaves[i], @@ -720,7 +826,7 @@ function MusicKeyboard() { } } - let sortedList = sortableList.sort(function (a, b) { + let sortedList = sortableList.sort((a, b) => { return a.frequency - b.frequency; }); @@ -747,7 +853,7 @@ function MusicKeyboard() { removeBlock(i); } - const newList = fillChromaticGaps(sortedList); + const newList = this.fillChromaticGaps(sortedList); for (let i = 0; i < newList.length; i++) { this.layout.push({ @@ -757,17 +863,24 @@ function MusicKeyboard() { voice: newList[i].voice }); } - }; + } + + /** + * @private + * @param {number} colIndex + * @param {boolean} playNote + * @returns {void} + */ - this._setNotes = function (colIndex, playNote) { + _setNotes(colIndex, playNote) { const start = docById("cells-" + colIndex).getAttribute("start"); - this._notesPlayed = this._notesPlayed.filter(function (ele) { + this._notesPlayed = this._notesPlayed.filter((ele) => { return ele.startTime != parseInt(start); }); // Look for each cell that is marked in this column. - silence = true; + let silence = true; let row, cell, ele, dur; for (let j = 0; j < this.layout.length; j++) { row = docById("mkb" + j); @@ -787,16 +900,25 @@ function MusicKeyboard() { objId: null, duration: parseFloat(dur) }); - this._notesPlayed.sort(function (a, b) { + this._notesPlayed.sort((a, b) => { return a.startTime - b.startTime; }); } - }; + } - this._setNoteCell = function (j, colIndex, start, playNote) { + /** + * @private + * @param {number} j + * @param {number} colIndex + * @param {string} start + * @param {boolean} playNote + * @returns {void} + */ + + _setNoteCell(j, colIndex, start, playNote) { const n = this.layout.length; const temp1 = this.layout[n - j - 1].noteName; - let temp2, ele; + let temp2; if (temp1 === "hertz") { temp2 = this.layout[n - j - 1].noteOctave; } else if (temp1 in FIXEDSOLFEGE1) { @@ -808,7 +930,7 @@ function MusicKeyboard() { temp1.replace(SHARP, "#").replace(FLAT, "b") + this.layout[n - j - 1].noteOctave; } - ele = docById(j + ":" + colIndex); + const ele = docById(j + ":" + colIndex); this._notesPlayed.push({ startTime: parseInt(start), noteOctave: temp2, @@ -818,12 +940,12 @@ function MusicKeyboard() { voice: this.layout[n - j - 1].voice }); - this._notesPlayed.sort(function (a, b) { + this._notesPlayed.sort((a, b) => { return a.startTime - b.startTime; }); if (playNote) { - this._logo.synth.trigger( + logo.synth.trigger( 0, temp2, ele.getAttribute("alt"), @@ -832,13 +954,18 @@ function MusicKeyboard() { null ); } - }; + } - this.makeClickable = function () { + /** + * @public + * @returns {void} + */ + + makeClickable() { const rowNote = docById("mkbNoteDurationRow"); let cell; - for (let i = 0; i < selectedNotes.length; i++) { + for (let i = 0; i < MusicKeyboard.selectedNotes.length; i++) { cell = rowNote.cells[i]; cell.onclick = (event) => { @@ -864,11 +991,10 @@ function MusicKeyboard() { cell.onmousedown = (e) => { cell = e.target; - let obj, i, j; isMouseDown = true; - obj = cell.id.split(":"); - i = Number(obj[0]); - j = Number(obj[1]); + const obj = cell.id.split(":"); + // const i = Number(obj[0]); + const j = Number(obj[1]); if (cell.style.backgroundColor === "black") { cell.style.backgroundColor = cell.getAttribute("cellColor"); this._setNotes(j, false); @@ -879,10 +1005,9 @@ function MusicKeyboard() { }; cell.onmouseover = () => { - let obj, i, j; - obj = cell.id.split(":"); - i = Number(obj[0]); - j = Number(obj[1]); + const obj = cell.id.split(":"); + // const i = Number(obj[0]); + const j = Number(obj[1]); if (isMouseDown) { if (cell.style.backgroundColor === "black") { cell.style.backgroundColor = cell.getAttribute("cellColor"); @@ -894,18 +1019,29 @@ function MusicKeyboard() { } }; - cell.onmouseup = function () { + cell.onmouseup = () => { isMouseDown = false; }; } } - }; + } + + /** + * @private + * @param {number} noteValue + * @returns {number} + */ - this._noteWidth = function (noteValue) { + _noteWidth(noteValue) { return Math.max(Math.floor(EIGHTHNOTEWIDTH * (8 * noteValue) * this._cellScale), 15); - }; + } + + /** + * @private + * @returns {void} + */ - this._createTable = function () { + _createTable() { this.processSelected(); const mkbTableDiv = this.keyTable; mkbTableDiv.style.display = "inline"; @@ -934,7 +1070,7 @@ function MusicKeyboard() { docById("mkbInnerDiv").style.marginLeft = 0; const mkbTable = docById("mkbTable"); - if (selectedNotes.length < 1) { + if (MusicKeyboard.selectedNotes.length < 1) { outerDiv.innerHTML = ""; return; } @@ -1010,10 +1146,11 @@ function MusicKeyboard() { '