From 7fcfac6032636bc62e8dba8e94cc3e0e0b6f8cb9 Mon Sep 17 00:00:00 2001 From: ricknjacky Date: Sat, 30 Jan 2021 15:55:20 +0530 Subject: [PATCH 1/7] WidgetBlocks for rhythm --- js/blocks/WidgetBlocks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/blocks/WidgetBlocks.js b/js/blocks/WidgetBlocks.js index 93353168ba..d9a83dfdc7 100644 --- a/js/blocks/WidgetBlocks.js +++ b/js/blocks/WidgetBlocks.js @@ -894,7 +894,7 @@ function setupWidgetBlocks() { logo.setDispatchBlock(blk, turtle, listenerName); let __listener = function(event) { - logo.rhythmRuler = new RhythmRuler(); + logo.rhythmRuler.init(); }; logo.setTurtleListener(turtle, listenerName, __listener); From c4aa40365b561e772aa08c78d1cdac91a0592f15 Mon Sep 17 00:00:00 2001 From: ricknjacky Date: Sat, 30 Jan 2021 15:56:03 +0530 Subject: [PATCH 2/7] ES6 syntax port widgets/rhythm --- js/widgets/rhythmruler.js | 3238 +++++++++++++++++++------------------ 1 file changed, 1705 insertions(+), 1533 deletions(-) diff --git a/js/widgets/rhythmruler.js b/js/widgets/rhythmruler.js index 5078dbfd13..a1e5f0fc60 100644 --- a/js/widgets/rhythmruler.js +++ b/js/widgets/rhythmruler.js @@ -9,23 +9,13 @@ // License along with this library; if not, write to the Free Software // Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA -/* - global TONEBPM, Singer, logo, _, delayExecution, docById, calcNoteValueToDisplay, platformColor, - beginnerMode, last, EIGHTHNOTEWIDTH, nearestBeat, rationalToFraction, DRUMNAMES, VOICENAMES, - EFFECTSNAMES, wheelnav, slicePath - */ - -/* exported RhythmRuler */ - -/** - * @abstract - * This widget enable us to create a rhythms which can be imported into the pitch-time matrix and - * hence used to create chunks of notes. - * - * @description - * - `rulerButtonsDiv` is for the widget buttons - * - `rulerTableDiv` is for the drum buttons (fixed first col) and the ruler cells - */ +// This widget enable us to create a rhythms which can be imported +// into the pitch-time matrix and hence used to create chunks of +// notes. + +// rulerButtonsDiv is for the widget buttons +// rulerTableDiv is for the drum buttons (fixed first col) and the ruler cells + class RhythmRuler { static ROWHEIGHT = 130; static RULERHEIGHT = 70; @@ -33,757 +23,410 @@ class RhythmRuler { static ICONSIZE = 32; static DEL = 46; - constructor() { - // console.debug("init RhythmRuler"); - this._bpmFactor = (1000 * TONEBPM) / Singer.masterBPM; + constructor(){ + // There is one ruler per drum. + this.Drums = []; + // Rulers, one per drum, contain the subdivisions defined by rhythm blocks. + this.Rulers = []; + // Save the history of divisions so as to be able to restore them. + this._dissectHistory = []; + this._undoList = []; - this._playing = false; - this._playingOne = false; - this._playingAll = false; - this._rulerPlaying = -1; - this._startingTime = null; - this._expanded = false; + this._playing = false; + this._playingOne = false; + this._playingAll = false; + this._cellCounter = 0; - // There is one ruler per drum. - this.Drums = []; - // Rulers, one per drum, contain the subdivisions defined by rhythm blocks. - this.Rulers = []; - // Save the history of divisions so as to be able to restore them. - this._dissectHistory = []; - this._undoList = []; + // Keep a elapsed time for each ruler to maintain sync. + this._elapsedTimes = []; + // Starting time from which we measure for sync. + this._startingTime = null; - this._playing = false; - this._playingOne = false; - this._playingAll = false; - this._cellCounter = 0; + this._offsets = []; + this._rulerSelected = 0; + this._rulerPlaying = -1; - // Keep a elapsed time for each ruler to maintain sync. - this._elapsedTimes = []; - // Starting time from which we measure for sync. - this._startingTime = null; + this._tapMode = false; + this._tapTimes = []; + this._tapCell = null; + this._tapEndTime = null; - this._offsets = []; - this._rulerSelected = 0; - this._rulerPlaying = -1; + this._longPressStartTime = null; + this._inLongPress = false; - this._tapMode = false; - this._tapTimes = []; - this._tapCell = null; - this._tapEndTime = null; + this._mouseDownCell = null; + this._mouseUpCell = null; - this._longPressStartTime = null; - this._inLongPress = false; + this._wheel = null; - this._mouseDownCell = null; - this._mouseUpCell = null; + // Element references + this._dissectNumber = null; + this._progressBar = null; + this._rulers = []; + } - this._wheel = null; + _noteWidth (noteValue) { + return Math.floor(EIGHTHNOTEWIDTH * (8 / Math.abs(noteValue)) * 4); + }; - // Element references - this._dissectNumber = null; - this._progressBar = null; - this._rulers = []; + _calculateZebraStripes (rulerno) { + let ruler = this._rulers[rulerno]; + let evenColor; + if (this._rulerSelected % 2 === 0) { + evenColor = platformColor.selectorBackground; + } else { + evenColor = platformColor.selectorSelected; + } - // If there are no drums, add one. - if (this.Drums.length === 0) { - this.Drums.push(null); - this.Rulers.push([[1], []]); + for (let i = 0; i < ruler.cells.length; i++) { + let newCell = ruler.cells[i]; + newCell.style.border = "2px solid lightgrey"; + newCell.style.borderRadius = "10px"; + if (evenColor === platformColor.selectorBackground) { + if (i % 2 === 0) { + newCell.style.backgroundColor = + platformColor.selectorBackground; + } else { + newCell.style.backgroundColor = + platformColor.selectorSelected; + } + } + + if (evenColor === platformColor.selectorSelected) { + if (i % 2 === 0) { + newCell.style.backgroundColor = + platformColor.selectorSelected; + } else { + newCell.style.backgroundColor = + platformColor.selectorBackground; + } + } } + }; - this._elapsedTimes = []; - this._offsets = []; - for (let i = 0; i < this.Rulers.length; i++) { - this._elapsedTimes.push(0); - this._offsets.push(0); + _dissectRuler (event, ruler) { + let cell = event.target; + if (cell === null) { + return; } - this._cellScale = 1.0; + if (this._tapMode && this._tapTimes.length > 0) { + let d = new Date(); + this._tapTimes.push(d.getTime()); + return; + } - const widgetWindow = window.widgetWindows.windowFor(this, "rhythm maker"); - this.widgetWindow = widgetWindow; - widgetWindow.clear(); - widgetWindow.show(); + let cellParent = cell.parentNode; + if (cellParent === null) { + return; + } - // For the button callbacks + this._rulerSelected = ruler; + if (this._rulerSelected == undefined) { + return; + } - widgetWindow.onclose = () => { - // If the piemenu was open, close it. - // docById('wheelDiv').style.display = 'none'; - // docById('contextWheelDiv').style.display = 'none'; + if (this._playing) { + console.warn("You cannot dissect while widget is playing."); + return; + } else if (this._tapMode) { + // Tap a rhythm by clicking in a cell. + if (this._tapCell === null) { + let noteValues = this.Rulers[this._rulerSelected][0]; + this._tapCell = event.target; + if (noteValues[this._tapCell.cellIndex] < 0) { + // Don't allow tapping in rests. + this._tapCell = null; + this._tapMode = false; + this._tapTimes = []; + this._tapEndTime = null; + let iconSize = ICONSIZE; + this._tapButton.innerHTML = + '' +
+                        _('; + return; + } - // Save the new dissect history. - const dissectHistory = []; - const drums = []; - for (let i = 0; i < this.Rulers.length; i++) { - if (this.Drums[i] === null) { - continue; + this._tapTimes = []; + + // Play a count off before starting tapping. + let interval = + this._bpmFactor / + Math.abs(noteValues[this._tapCell.cellIndex]); + + let drum; + if (this.Drums[this._rulerSelected] === null) { + drum = "snare drum"; + } else { + let drumBlockNo = logo.blocks.blockList[ + this.Drums[this._rulerSelected] + ].connections[1]; + drum = logo.blocks.blockList[drumBlockNo].value; } - const history = []; - for (let j = 0; j < this.Rulers[i][1].length; j++) { - history.push(this.Rulers[i][1][j]); + + // FIXME: Should be based on meter + for (let i = 0; i < 4; i++) { + setTimeout(function () { + logo.synth.trigger( + 0, "C4", Singer.defaultBPMFactor / 16, drum, null, null + ); + }, (interval * i) / 4); } - this._dissectNumber.classList.add("hasKeyboard"); - dissectHistory.push([history, this.Drums[i]]); - drums.push(this.Drums[i]); + setTimeout(function () { + this.__startTapping(noteValues, interval); + }, interval); } - - // Look for any old entries that we may have missed. - for (let i = 0; i < this._dissectHistory.length; i++) { - const drum = this._dissectHistory[i][1]; - if (drums.indexOf(drum) === -1) { - const history = JSON.parse(JSON.stringify(this._dissectHistory[i][0])); - dissectHistory.push([history, drum]); - } + } else { + let noteValues = this.Rulers[this._rulerSelected][0]; + let inputNum = this._dissectNumber.value; + if (inputNum === "" || isNaN(inputNum)) { + inputNum = 2; + } else { + inputNum = Math.abs(Math.floor(inputNum)); } - this._dissectHistory = JSON.parse(JSON.stringify(dissectHistory)); + this._dissectNumber.value = inputNum; - this._playing = false; - this._playingOne = false; - this._playingAll = false; - logo.hideMsgs(); + this._rulerSelected = cell.parentNode.getAttribute("data-row"); + this.__dissectByNumber(cell, inputNum, true); + } - this.widgetWindow.destroy(); - }; + // this._piemenuRuler(this._rulerSelected); - this._playAllCell = widgetWindow.addButton( - "play-button.svg", - RhythmRuler.ICONSIZE, - _("Play all") - ); - this._playAllCell.onclick = () => { - if (this._playing) { - this.__pause(); - } else if (!this._playingAll) { - this.__resume(); - } - }; + //Save dissect history everytime user dissects ruler + this.saveDissectHistory(); + }; - this._save_lock = false; - widgetWindow.addButton( - "export-chunk.svg", - RhythmRuler.ICONSIZE, - _("Save rhythms") - ).onclick = async () => { - // that._save(0); - // Debounce button - if (!this._get_save_lock()) { - this._save_lock = true; + __startTapping (noteValues, interval) { + let d = new Date(); + this._tapTimes = [d.getTime()]; + this._tapEndTime = this._tapTimes[0] + interval; - // Save a merged version of the rulers. - this._saveTupletsMerged(this._mergeRulers()); + // Set a timeout to end tapping + + setTimeout(function () { + this.__endTapping(); + }, interval); - // Rather than each ruler individually. - // that._saveTuplets(0); - await delayExecution(1000); - this._save_lock = false; - } - }; + // Display a progress bar. + function __move(tick, stepSize) { + let width = 1; + let id = setInterval(frame, tick); - widgetWindow.addButton( - "export-drums.svg", - RhythmRuler.ICONSIZE, - _("Save drum machine") - ).onclick = async () => { - // Debounce button - if (!this._get_save_lock()) { - this._save_lock = true; - this._saveMachine(0); - await delayExecution(1000); - this._save_lock = false; + function frame() { + if (width >= 100) { + clearInterval(id); + } else { + width += stepSize; + this._progressBar.style.width = width + "%"; + } } - }; - - // An input for setting the dissect number - this._dissectNumber = widgetWindow.addInputButton("2"); - - this._dissectNumber.onfocus = () => { - // that._piemenuNumber(['2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16'], numberInput.value); - }; + } - this._dissectNumber.onkeydown = (event) => { - // 46 number is for the delete keypress - if (event.keyCode === 46) { - this._dissectNumber.value = this._dissectNumber.value.substring( - 0, - this._dissectNumber.value.length - 1 - ); - } - }; + this._tapCell.innerHTML = "
"; + this._progressBar = this._tapCell.querySelector(".progressBar"); + // Progress once per 8th note. + __move(interval / 8, 100 / 8); + }; - this._dissectNumber.oninput = () => { - // Put a limit on the size (2 <--> 128). - this._dissectNumber.onmouseout = () => { - this._dissectNumber.value = Math.max(this._dissectNumber.value, 2); - }; + __endTapping () { + if (cell.parentNode === null) { + console.debug("Null parent node in endTapping"); + return; + } - this._dissectNumber.value = Math.max(Math.min(this._dissectNumber.value, 128), 2); - }; + this._rulerSelected = cell.parentNode.getAttribute("data-row"); + if (this._progressBar) this._progressBar.remove(); + this._tapCell.innerHTML = ""; - widgetWindow.addButton( - "restore-button.svg", - RhythmRuler.ICONSIZE, - _("Undo") - ).onclick = () => { - this._undo(); - }; + let d = new Date(); + this._tapTimes.push(d.getTime()); - //.TRANS: user can tap out a rhythm by clicking on a ruler. - this._tapButton = widgetWindow.addButton( - "tap-button.svg", - RhythmRuler.ICONSIZE, - _("Tap a rhythm") - ); - this._tapButton.onclick = () => { - this._tap(); - }; + this._tapMode = false; + if ( + typeof this._rulerSelected === "string" || + typeof this._rulerSelected === "number" + ) { + let noteValues = this.Rulers[this._rulerSelected][0]; - //.TRANS: clear all subdivisions from the ruler. - widgetWindow.addButton( - "erase-button.svg", - RhythmRuler.ICONSIZE, - _("Clear") - ).onclick = () => { - this._clear(); - }; + if (last(this._tapTimes) > this._tapEndTime) { + this._tapTimes[this._tapTimes.length - 1] = this._tapEndTime; + } - // We use an outer div to scroll vertically and an inner div to - // scroll horizontally. - const rhythmRulerTable = document.createElement("table"); - widgetWindow.getWidgetBody().append(rhythmRulerTable); + // convert times into cells here. + let inputNum = this._dissectNumber.value; + if (inputNum === "" || isNaN(inputNum)) { + inputNum = 2; + } else { + inputNum = Math.abs(Math.floor(inputNum)); + } - let wMax = 0; - // Each row in the ruler table contains a play button in the - // first column and a ruler table in the second column. - for (let i = 0; i < this.Rulers.length; i++) { - const rhythmRulerTableRow = rhythmRulerTable.insertRow(); - - if (beginnerMode) { - let w = 0; - for (let r = 0; r < this.Rulers[i][0].length; r++) { - w += 580 / this.Rulers[i][0][r]; - } - - if (w > wMax) { - rhythmRulerTable.style.width = w + "px"; - wMax = w; - } - } else { - const drumcell = rhythmRulerTableRow.insertCell(); - drumcell.innerHTML = - '' +
-                    _('; - drumcell.className = "headcol"; // Position fixed when scrolling horizontally - - drumcell.onclick = ((id) => { - return () => { - if (this._playing) { - if (this._rulerPlaying === id) { - this.innerHTML = - '' +
-                                    _('; - this._playing = false; - this._playingOne = false; - this._playingAll = false; - this._rulerPlaying = -1; - this._startingTime = null; - this._elapsedTimes[id] = 0; - this._offsets[id] = 0; - setTimeout(this._calculateZebraStripes(id), 1000); - } - } else { - if (this._playingOne === false) { - this._rulerSelected = id; - logo.turtleDelay = 0; - this._playing = true; - this._playingOne = true; - this._playingAll = false; - this._cellCounter = 0; - this._startingTime = null; - this._rulerPlaying = id; - this.innerHTML = - '' +
-                                    _('; - this._elapsedTimes[id] = 0; - this._offsets[id] = 0; - this._playOne(); - } - } - }; - })(i); + // Minimum beat is tied to the input number + let minimumBeat; + switch (inputNum) { + case 2: + minimumBeat = 16; + break; + case 3: + minimumBeat = 27; + break; + case 4: + minimumBeat = 32; + break; + case 5: + minimumBeat = 25; + break; + case 6: + minimumBeat = 36; + break; + case 7: + minimumBeat = 14; + break; + case 8: + minimumBeat = 64; + break; + default: + minimumBeat = 16; + break; } - const rulerCell = rhythmRulerTableRow.insertCell(); - // Create individual rulers as tables. - rulerCell.innerHTML = '
'; - - const rulerCellTable = docById("rulerCellTable" + i); - rulerCellTable.style.textAlign = "center"; - rulerCellTable.style.border = "0px"; - rulerCellTable.style.borderCollapse = "collapse"; - rulerCellTable.cellSpacing = "0px"; - rulerCellTable.cellPadding = "0px"; - const rulerRow = rulerCellTable.insertRow(); - this._rulers[i] = rulerRow; - rulerRow.setAttribute("data-row", i); + let newNoteValues = []; + let sum = 0; + let obj; + let interval = + this._bpmFactor / Math.abs(noteValues[this._tapCell.cellIndex]); + for (let i = 1; i < this._tapTimes.length; i++) { + let dtime = this._tapTimes[i] - this._tapTimes[i - 1]; + if (i < this._tapTimes.length - 1) { + obj = nearestBeat( + (100 * dtime) / this._bpmFactor, + minimumBeat + ); + if (obj[0] === 0) { + obj[0] = 1; + obj[1] = obj[1] / 2; + } - for (let j = 0; j < this.Rulers[i][0].length; j++) { - const noteValue = this.Rulers[i][0][j]; - const rulerSubCell = rulerRow.insertCell(-1); - rulerSubCell.innerHTML = calcNoteValueToDisplay(noteValue, 1, this._cellScale); - rulerSubCell.style.height = RhythmRuler.RULERHEIGHT + "px"; - rulerSubCell.style.minHeight = rulerSubCell.style.height; - rulerSubCell.style.maxHeight = rulerSubCell.style.height; - rulerSubCell.style.width = this._noteWidth(noteValue) + "px"; - rulerSubCell.style.minWidth = rulerSubCell.style.width; - rulerSubCell.style.border = "0px"; - rulerSubCell.border = "0px"; - rulerSubCell.padding = "0px"; - rulerSubCell.style.padding = "0px"; - rulerSubCell.style.lineHeight = 60 + " % "; - if (i % 2 === 0) { - if (j % 2 === 0) { - rulerSubCell.style.backgroundColor = platformColor.selectorBackground; - } else { - rulerSubCell.style.backgroundColor = platformColor.selectorSelected; + if (sum + obj[0] / obj[1] < 1) { + sum += obj[0] / obj[1]; + newNoteValues.push(obj[1] / obj[0]); } } else { - if (j % 2 === 0) { - rulerSubCell.style.backgroundColor = platformColor.selectorSelected; - } else { - rulerSubCell.style.backgroundColor = platformColor.selectorBackground; - } + // Since the fractional value is noisy, + // ensure that the final beat make the + // total add up to the proper note value. + obj = rationalToFraction( + 1 / noteValues[this._tapCell.cellIndex] - sum + ); + newNoteValues.push(obj[1] / obj[0]); } - - this.__addCellEventHandlers(rulerSubCell, this._noteWidth(noteValue), noteValue); } - // Match the play button height to the ruler height. - rhythmRulerTableRow.cells[0].style.width = RhythmRuler.BUTTONSIZE + "px"; - rhythmRulerTableRow.cells[0].style.minWidth = RhythmRuler.BUTTONSIZE + "px"; - rhythmRulerTableRow.cells[0].style.maxWidth = RhythmRuler.BUTTONSIZE + "px"; - rhythmRulerTableRow.cells[0].style.height = rulerRow.offsetHeight + "px"; - rhythmRulerTableRow.cells[0].style.minHeight = rulerRow.offsetHeight + "px"; - rhythmRulerTableRow.cells[0].style.maxHeight = rulerRow.offsetHeight + "px"; - rhythmRulerTableRow.cells[0].style.verticalAlign = "middle"; + this.__divideFromList(this._tapCell, newNoteValues, true); } - // Restore dissect history. - let cell; - for (let drum = 0; drum < this.Drums.length; drum++) { - if (drum === null) { - continue; - } + this._tapTimes = []; + this._tapCell = null; + this._tapEndTime = null; + let iconSize = ICONSIZE; + this._tapButton.innerHTML = + '' +
+            _('; + }; - for (let i = 0; i < this._dissectHistory.length; i++) { - if (this._dissectHistory[i][1] !== this.Drums[drum]) { - continue; - } + __addCellEventHandlers (cell, cellWidth, noteValue) { + - const rhythmRulerTableRow = this._rulers[drum]; - for (let j = 0; j < this._dissectHistory[i][0].length; j++) { - if (this._dissectHistory[i][0][j] == undefined) { - continue; - } + __mouseOverHandler = function (event) { + let cell = event.target; + if (cell === null || cell.parentNode === null) { + return; + } - this._rulerSelected = drum; + this._rulerSelected = cell.parentNode.getAttribute("data-row"); + let noteValues = this.Rulers[this._rulerSelected][0]; + let noteValue = noteValues[cell.cellIndex]; + let obj; + if (noteValue < 0) { + obj = rationalToFraction( + Math.abs(Math.abs(-1 / noteValue)) + ); + cell.innerHTML = + calcNoteValueToDisplay(obj[1], obj[0], this._cellScale) + + " " + + _("silence"); + } else { + obj = rationalToFraction(Math.abs(Math.abs(1 / noteValue))); + cell.innerHTML = calcNoteValueToDisplay( + obj[1], + obj[0], + this._cellScale + ); + } + }; - if (typeof this._dissectHistory[i][0][j] === "number") { - cell = rhythmRulerTableRow.cells[this._dissectHistory[i][0][j]]; - this.__toggleRestState(cell, false); - } else if (typeof this._dissectHistory[i][0][j][0] === "number") { - if (typeof this._dissectHistory[i][0][j][1] === "number") { - // dissect is [cell, num] - cell = rhythmRulerTableRow.cells[this._dissectHistory[i][0][j][0]]; - if (cell != undefined) { - this.__dissectByNumber( - cell, - this._dissectHistory[i][0][j][1], - false - ); - } - // else { - // console.warn( - // "Could not find cell to divide. Did the order of the rhythm blocks change?" - // ); - // } - } else { - // divide is [cell, [values]] - cell = rhythmRulerTableRow.cells[this._dissectHistory[i][0][j][0]]; - if (cell != undefined) { - this.__divideFromList( - cell, - this._dissectHistory[i][0][j][1], - false - ); - } - } - } else { - // tie is [[cell, value], [cell, value]...] - const history = this._dissectHistory[i][0][j]; - this._mouseDownCell = rhythmRulerTableRow.cells[history[0][0]]; - this._mouseUpCell = rhythmRulerTableRow.cells[last(history)[0]]; - if (this._mouseUpCell != undefined) { - this.__tie(false); - } + __mouseOutHandler = function (event) { + let cell = event.target; + cell.innerHTML = ""; + }; - this._mouseDownCell = null; - this._mouseUpCell = null; - } - } - } - } + __mouseDownHandler = function (event) { + let cell = event.target; + this._mouseDownCell = cell; - logo.textMsg(_("Click on the ruler to divide it.")); - // this._piemenuRuler(this._rulerSelected); - } + let d = new Date(); + this._longPressStartTime = d.getTime(); + this._inLongPress = false; - _noteWidth(noteValue) { - return Math.floor(EIGHTHNOTEWIDTH * (8 / Math.abs(noteValue)) * 4); - } + this._longPressBeep = setTimeout(function () { + // Removing audio feedback on long press since it + // occasionally confuses tone.js during rapid clicking + // in the widget. - _calculateZebraStripes(rulerno) { - const ruler = this._rulers[rulerno]; - let evenColor; - if (this._rulerSelected % 2 === 0) { - evenColor = platformColor.selectorBackground; - } else { - evenColor = platformColor.selectorSelected; - } + // logo.synth.trigger(0, 'C4', 1 / 32, 'chime', null, null); - for (let i = 0; i < ruler.cells.length; i++) { - const newCell = ruler.cells[i]; - newCell.style.border = "2px solid lightgrey"; - newCell.style.borderRadius = "10px"; - if (evenColor === platformColor.selectorBackground) { - if (i % 2 === 0) { - newCell.style.backgroundColor = platformColor.selectorBackground; - } else { - newCell.style.backgroundColor = platformColor.selectorSelected; + let cell = this._mouseDownCell; + if (cell !== null && cell.parentNode !== null) { + this._rulerSelected = cell.parentNode.getAttribute( + "data-row" + ); + let noteValues = this.Rulers[this._rulerSelected][0]; + let noteValue = noteValues[cell.cellIndex]; + cell.style.backgroundColor = + platformColor.selectorBackground; } - } + }, 1500); + }; - if (evenColor === platformColor.selectorSelected) { - if (i % 2 === 0) { - newCell.style.backgroundColor = platformColor.selectorSelected; - } else { - newCell.style.backgroundColor = platformColor.selectorBackground; - } - } - } - } - - _dissectRuler(event, ruler) { - const cell = event.target; - if (cell === null) { - return; - } - - if (this._tapMode && this._tapTimes.length > 0) { - const d = new Date(); - this._tapTimes.push(d.getTime()); - return; - } - - const cellParent = cell.parentNode; - if (cellParent === null) { - return; - } - - this._rulerSelected = ruler; - if (this._rulerSelected == undefined) { - return; - } - - if (this._playing) { - // console.warn("You cannot dissect while widget is playing."); - return; - } else if (this._tapMode) { - // Tap a rhythm by clicking in a cell. - if (this._tapCell === null) { - const noteValues = this.Rulers[this._rulerSelected][0]; - this._tapCell = event.target; - if (noteValues[this._tapCell.cellIndex] < 0) { - // Don't allow tapping in rests. - this._tapCell = null; - this._tapMode = false; - this._tapTimes = []; - this._tapEndTime = null; - this._tapButton.innerHTML = - '' +
-                        _('; - return; - } - - this._tapTimes = []; - - // Play a count off before starting tapping. - const interval = this._bpmFactor / Math.abs(noteValues[this._tapCell.cellIndex]); - - let drum; - if (this.Drums[this._rulerSelected] === null) { - drum = "snare drum"; - } else { - const drumBlockNo = - logo.blocks.blockList[this.Drums[this._rulerSelected]].connections[1]; - drum = logo.blocks.blockList[drumBlockNo].value; - } - - // FIXME: Should be based on meter - for (let i = 0; i < 4; i++) { - setTimeout(() => { - logo.synth.trigger(0, "C4", Singer.defaultBPMFactor / 16, drum, null, null); - }, (interval * i) / 4); - } - - setTimeout(() => { - this.__startTapping(noteValues, interval, event); - }, interval); - } - } else { - let inputNum = this._dissectNumber.value; - if (inputNum === "" || isNaN(inputNum)) { - inputNum = 2; - } else { - inputNum = Math.abs(Math.floor(inputNum)); - } - - this._dissectNumber.value = inputNum; - - this._rulerSelected = cell.parentNode.getAttribute("data-row"); - this.__dissectByNumber(cell, inputNum, true); - } - - // this._piemenuRuler(this._rulerSelected); - - //Save dissect history everytime user dissects ruler - this.saveDissectHistory(); - } - - __startTapping(noteValues, interval, event) { - const d = new Date(); - this._tapTimes = [d.getTime()]; - this._tapEndTime = this._tapTimes[0] + interval; - - // Set a timeout to end tapping - setTimeout(() => { - this.__endTapping(event); - }, interval); - - // Display a progress bar. - const __move = (tick, stepSize) => { - let width = 1; - - const id = setInterval(() => { - if (width >= 100) { - clearInterval(id); - } else { - width += stepSize; - this._progressBar.style.width = width + "%"; - } - }, tick); - }; - - this._tapCell.innerHTML = "
"; - this._progressBar = this._tapCell.querySelector(".progressBar"); - // Progress once per 8th note. - __move(interval / 8, 100 / 8); - } - - __endTapping(event) { - const cell = event.target; - if (cell.parentNode === null) { - // console.debug("Null parent node in endTapping"); - return; - } - - this._rulerSelected = cell.parentNode.getAttribute("data-row"); - if (this._progressBar) this._progressBar.remove(); - this._tapCell.innerHTML = ""; - - const d = new Date(); - this._tapTimes.push(d.getTime()); - - this._tapMode = false; - if (typeof this._rulerSelected === "string" || typeof this._rulerSelected === "number") { - const noteValues = this.Rulers[this._rulerSelected][0]; - - if (last(this._tapTimes) > this._tapEndTime) { - this._tapTimes[this._tapTimes.length - 1] = this._tapEndTime; - } - - // convert times into cells here. - let inputNum = this._dissectNumber.value; - if (inputNum === "" || isNaN(inputNum)) { - inputNum = 2; - } else { - inputNum = Math.abs(Math.floor(inputNum)); - } - - // Minimum beat is tied to the input number - let minimumBeat; - switch (inputNum) { - case 2: - minimumBeat = 16; - break; - case 3: - minimumBeat = 27; - break; - case 4: - minimumBeat = 32; - break; - case 5: - minimumBeat = 25; - break; - case 6: - minimumBeat = 36; - break; - case 7: - minimumBeat = 14; - break; - case 8: - minimumBeat = 64; - break; - default: - minimumBeat = 16; - break; - } - - const newNoteValues = []; - let sum = 0; - let obj; - for (let i = 1; i < this._tapTimes.length; i++) { - const dtime = this._tapTimes[i] - this._tapTimes[i - 1]; - if (i < this._tapTimes.length - 1) { - obj = nearestBeat((100 * dtime) / this._bpmFactor, minimumBeat); - if (obj[0] === 0) { - obj[0] = 1; - obj[1] = obj[1] / 2; - } - - if (sum + obj[0] / obj[1] < 1) { - sum += obj[0] / obj[1]; - newNoteValues.push(obj[1] / obj[0]); - } - } else { - // Since the fractional value is noisy, - // ensure that the final beat make the - // total add up to the proper note value. - obj = rationalToFraction(1 / noteValues[this._tapCell.cellIndex] - sum); - newNoteValues.push(obj[1] / obj[0]); - } - } - - this.__divideFromList(this._tapCell, newNoteValues, true); - } - - this._tapTimes = []; - this._tapCell = null; - this._tapEndTime = null; - this._tapButton.innerHTML = - '' +
-            _('; - } - - __addCellEventHandlers(cell, cellWidth, noteValue) { - const __mouseOverHandler = (event) => { - const cell = event.target; - if (cell === null || cell.parentNode === null) { - return; - } - - this._rulerSelected = cell.parentNode.getAttribute("data-row"); - const noteValues = this.Rulers[this._rulerSelected][0]; - const noteValue = noteValues[cell.cellIndex]; - let obj; - if (noteValue < 0) { - obj = rationalToFraction(Math.abs(Math.abs(-1 / noteValue))); - cell.innerHTML = - calcNoteValueToDisplay(obj[1], obj[0], this._cellScale) + " " + _("silence"); - } else { - obj = rationalToFraction(Math.abs(Math.abs(1 / noteValue))); - cell.innerHTML = calcNoteValueToDisplay(obj[1], obj[0], this._cellScale); - } - }; - - const __mouseOutHandler = (event) => { - const cell = event.target; - cell.innerHTML = ""; - }; - - const __mouseDownHandler = (event) => { - const cell = event.target; - this._mouseDownCell = cell; - - const d = new Date(); - this._longPressStartTime = d.getTime(); - this._inLongPress = false; - - this._longPressBeep = setTimeout(() => { - // Removing audio feedback on long press since it - // occasionally confuses tone.js during rapid clicking - // in the widget. - - // that._logo.synth.trigger(0, 'C4', 1 / 32, 'chime', null, null); - - const cell = this._mouseDownCell; - if (cell !== null && cell.parentNode !== null) { - this._rulerSelected = cell.parentNode.getAttribute("data-row"); - cell.style.backgroundColor = platformColor.selectorBackground; - } - }, 1500); - }; - - const __mouseUpHandler = (event) => { - clearTimeout(this._longPressBeep); - const cell = event.target; - this._mouseUpCell = cell; - if (this._mouseDownCell !== this._mouseUpCell) { - this._tieRuler(event, cell.parentNode.getAttribute("data-row")); - } else if (this._longPressStartTime !== null && !this._tapMode) { - const d = new Date(); - const elapseTime = d.getTime() - this._longPressStartTime; - if (elapseTime > 1500) { - this._inLongPress = true; - this.__toggleRestState(this, true); + __mouseUpHandler = function (event) { + clearTimeout(this._longPressBeep); + let cell = event.target; + this._mouseUpCell = cell; + if (this._mouseDownCell !== this._mouseUpCell) { + this._tieRuler(event, cell.parentNode.getAttribute("data-row")); + } else if (this._longPressStartTime !== null && !this._tapMode) { + let d = new Date(); + let elapseTime = d.getTime() - this._longPressStartTime; + if (elapseTime > 1500) { + this._inLongPress = true; + this.__toggleRestState(this, true); } } @@ -792,16 +435,18 @@ class RhythmRuler { this._longPressStartTime = null; }; - const __clickHandler = (event) => { + __clickHandler = function (event) { if (event == undefined) return; if (!this.__getLongPressStatus()) { - const cell = event.target; + let cell = event.target; if (cell !== null && cell.parentNode !== null) { - this._dissectRuler(event, cell.parentNode.getAttribute("data-row")); + this._dissectRuler( + event, + cell.parentNode.getAttribute("data-row") + ); + } else { + console.error("Rhythm Ruler: null cell found on click"); } - // else { - // console.error("Rhythm Ruler: null cell found on click"); - // } } this._inLongPress = false; @@ -810,7 +455,11 @@ class RhythmRuler { let obj; if (cellWidth > 12 && noteValue > 0) { obj = rationalToFraction(Math.abs(1 / noteValue)); - cell.innerHTML = calcNoteValueToDisplay(obj[1], obj[0], this._cellScale); + cell.innerHTML = calcNoteValueToDisplay( + obj[1], + obj[0], + this._cellScale + ); } else { cell.innerHTML = ""; @@ -829,20 +478,22 @@ class RhythmRuler { cell.removeEventListener("click", __clickHandler); cell.addEventListener("click", __clickHandler); - } + }; - __getLongPressStatus() { + __getLongPressStatus () { return this._inLongPress; - } + }; + + __toggleRestState (cell, addToUndoList) { + - __toggleRestState(cell, addToUndoList) { if (cell !== null && cell.parentNode !== null) { this._rulerSelected = cell.parentNode.getAttribute("data-row"); - const noteValues = this.Rulers[this._rulerSelected][0]; - const noteValue = noteValues[cell.cellIndex]; + let noteValues = this.Rulers[this._rulerSelected][0]; + let noteValue = noteValues[cell.cellIndex]; - const __mouseOverHandler = (event) => { - const cell = event.target; + __mouseOverHandler = function (event) { + let cell = event.target; if (cell === null) { return; } @@ -850,29 +501,45 @@ class RhythmRuler { let obj; this._rulerSelected = cell.parentNode.getAttribute("data-row"); - const noteValues = this.Rulers[this._rulerSelected][0]; - const noteValue = noteValues[cell.cellIndex]; + let noteValues = this.Rulers[this._rulerSelected][0]; + let noteValue = noteValues[cell.cellIndex]; if (noteValue < 0) { - obj = rationalToFraction(Math.abs(Math.abs(-1 / noteValue))); + obj = rationalToFraction( + Math.abs(Math.abs(-1 / noteValue)) + ); cell.innerHTML = - calcNoteValueToDisplay(obj[1], obj[0], this._cellScale) + + calcNoteValueToDisplay( + obj[1], + obj[0], + this._cellScale + ) + " " + _("silence"); } else { - obj = rationalToFraction(Math.abs(Math.abs(1 / noteValue))); - cell.innerHTML = calcNoteValueToDisplay(obj[1], obj[0], this._cellScale); + obj = rationalToFraction( + Math.abs(Math.abs(1 / noteValue)) + ); + cell.innerHTML = calcNoteValueToDisplay( + obj[1], + obj[0], + this._cellScale + ); } }; - const __mouseOutHandler = (event) => { - const cell = event.target; + __mouseOutHandler = function (event) { + let cell = event.target; cell.innerHTML = ""; }; let obj; if (noteValue < 0) { obj = rationalToFraction(Math.abs(1 / noteValue)); - cell.innerHTML = calcNoteValueToDisplay(obj[1], obj[0], this._cellScale); + cell.innerHTML = calcNoteValueToDisplay( + obj[1], + obj[0], + this._cellScale + ); cell.removeEventListener("mouseover", __mouseOverHandler); cell.removeEventListener("mouseout", __mouseOutHandler); } else { @@ -889,7 +556,7 @@ class RhythmRuler { this._calculateZebraStripes(this._rulerSelected); - const divisionHistory = this.Rulers[this._rulerSelected][1]; + divisionHistory = this.Rulers[this._rulerSelected][1]; if (addToUndoList) { this._undoList.push(["rest", this._rulerSelected]); } @@ -898,9 +565,9 @@ class RhythmRuler { } // this._piemenuRuler(this._rulerSelected); - } + }; - __divideFromList(cell, newNoteValues, addToUndoList) { + __divideFromList (cell, newNoteValues, addToUndoList) { if (typeof cell !== "object") { return; } @@ -909,13 +576,16 @@ class RhythmRuler { return; } - const ruler = this._rulers[this._rulerSelected]; - const newCellIndex = cell.cellIndex; + let ruler = this._rulers[this._rulerSelected]; + let newCellIndex = cell.cellIndex; - if (typeof this._rulerSelected === "string" || typeof this._rulerSelected === "number") { - const noteValues = this.Rulers[this._rulerSelected][0]; + if ( + typeof this._rulerSelected === "string" || + typeof this._rulerSelected === "number" + ) { + let noteValues = this.Rulers[this._rulerSelected][0]; - const divisionHistory = this.Rulers[this._rulerSelected][1]; + let divisionHistory = this.Rulers[this._rulerSelected][1]; if (addToUndoList) { this._undoList.push(["tap", this._rulerSelected]); } @@ -923,31 +593,37 @@ class RhythmRuler { divisionHistory.push([newCellIndex, newNoteValues]); ruler.deleteCell(newCellIndex); - // let tempwidth = this._noteWidth(newNoteValue); + + let noteValue = noteValues[newCellIndex]; + let tempwidth = this._noteWidth(newNoteValue); noteValues.splice(newCellIndex, 1); for (let i = 0; i < newNoteValues.length; i++) { - const newCell = ruler.insertCell(newCellIndex + i); - const newNoteValue = newNoteValues[i]; - const newCellWidth = parseFloat(this._noteWidth(newNoteValue)); + let newCell = ruler.insertCell(newCellIndex + i); + let newNoteValue = newNoteValues[i]; + let newCellWidth = parseFloat(this._noteWidth(newNoteValue)); noteValues.splice(newCellIndex + i, 0, newNoteValue); newCell.style.width = newCellWidth + "px"; newCell.style.minWidth = newCell.style.width; - newCell.style.height = RhythmRuler.RULERHEIGHT + "px"; + newCell.style.height = RULERHEIGHT + "px"; newCell.style.minHeight = newCell.style.height; newCell.style.maxHeight = newCell.style.height; - this.__addCellEventHandlers(newCell, newCellWidth, newNoteValue); + this.__addCellEventHandlers( + newCell, + newCellWidth, + newNoteValue + ); } this._calculateZebraStripes(this._rulerSelected); } // this._piemenuRuler(this._rulerSelected); - } + }; - __dissectByNumber(cell, inputNum, addToUndoList) { + __dissectByNumber (cell, inputNum, addToUndoList) { if (typeof cell !== "object") { return; } @@ -956,21 +632,26 @@ class RhythmRuler { return; } - const ruler = this._rulers[this._rulerSelected]; - const newCellIndex = cell.cellIndex; + let ruler = this._rulers[this._rulerSelected]; + let newCellIndex = cell.cellIndex; - if (typeof this._rulerSelected === "string" || typeof this._rulerSelected === "number") { - const noteValues = this.Rulers[this._rulerSelected][0]; + if ( + typeof this._rulerSelected === "string" || + typeof this._rulerSelected === "number" + ) { + let noteValues = this.Rulers[this._rulerSelected][0]; - const noteValue = noteValues[newCellIndex]; + let noteValue = noteValues[newCellIndex]; if (inputNum * noteValue > 256) { - logo.errorMsg(_("Maximum value of 256 has been exceeded.")); + logo.errorMsg( + _("Maximum value of 256 has been exceeded.") + ); return; } else { logo.hideMsgs(); } - const divisionHistory = this.Rulers[this._rulerSelected][1]; + let divisionHistory = this.Rulers[this._rulerSelected][1]; if (addToUndoList) { this._undoList.push(["dissect", this._rulerSelected]); } @@ -983,36 +664,43 @@ class RhythmRuler { newNoteValue = inputNum * noteValue; - const tempwidth = this._noteWidth(newNoteValue); - const difference = + let tempwidth = this._noteWidth(newNoteValue); + let tempwidthPixels = + parseFloat(inputNum) * parseFloat(tempwidth) + "px"; + let difference = parseFloat(this._noteWidth(noteValue)) - parseFloat(inputNum) * parseFloat(tempwidth); - const newCellWidth = - parseFloat(this._noteWidth(newNoteValue)) + parseFloat(difference) / inputNum; + let newCellWidth = + parseFloat(this._noteWidth(newNoteValue)) + + parseFloat(difference) / inputNum; noteValues.splice(newCellIndex, 1); for (let i = 0; i < inputNum; i++) { - const newCell = ruler.insertCell(newCellIndex + i); + let newCell = ruler.insertCell(newCellIndex + i); noteValues.splice(newCellIndex + i, 0, newNoteValue); newCell.style.width = newCellWidth + "px"; newCell.style.minWidth = newCell.style.width; - newCell.style.height = RhythmRuler.RULERHEIGHT + "px"; + newCell.style.height = RULERHEIGHT + "px"; newCell.style.minHeight = newCell.style.height; newCell.style.maxHeight = newCell.style.height; - this.__addCellEventHandlers(newCell, newCellWidth, newNoteValue); + this.__addCellEventHandlers( + newCell, + newCellWidth, + newNoteValue + ); } this._calculateZebraStripes(this._rulerSelected); } // this._piemenuRuler(this._rulerSelected); - } + }; - _tieRuler(event, ruler) { + _tieRuler (event, ruler) { if (this._playing) { - // console.warn("You cannot tie while widget is playing."); + console.warn("You cannot tie while widget is playing."); return; } else if (this._tapMode) { // If we are tapping, then treat a tie as a tap. @@ -1021,17 +709,17 @@ class RhythmRuler { } // Does this work if there are more than 10 rulers? - const cell = event.target; + let cell = event.target; if (cell !== null && cell.parentNode !== null) { this._rulerSelected = cell.parentNode.getAttribute("data-row"); this.__tie(true); } // this._piemenuRuler(this._rulerSelected); - } + }; - __tie(addToUndoList) { - const ruler = this._rulers[this._rulerSelected]; + __tie (addToUndoList) { + let ruler = this._rulers[this._rulerSelected]; if (this._mouseDownCell === null || this._mouseUpCell === null) { return; @@ -1041,7 +729,10 @@ class RhythmRuler { return; } - if (typeof this._rulerSelected === "string" || typeof this._rulerSelected === "number") { + if ( + typeof this._rulerSelected === "string" || + typeof this._rulerSelected === "number" + ) { let noteValues = this.Rulers[this._rulerSelected][0]; let downCellIndex = this._mouseDownCell.cellIndex; @@ -1062,19 +753,19 @@ class RhythmRuler { noteValues = this.Rulers[this._rulerSelected][0]; - const divisionHistory = this.Rulers[this._rulerSelected][1]; + let divisionHistory = this.Rulers[this._rulerSelected][1]; if (addToUndoList) { this._undoList.push(["tie", this._rulerSelected]); } - const history = []; + let history = []; for (let i = downCellIndex; i < upCellIndex + 1; i++) { history.push([i, noteValues[i]]); } divisionHistory.push(history); - const oldNoteValue = noteValues[downCellIndex]; + let oldNoteValue = noteValues[downCellIndex]; let noteValue = Math.abs(1 / oldNoteValue); // Delete all the cells between down and up except the down @@ -1085,7 +776,7 @@ class RhythmRuler { this.Rulers[this._rulerSelected][0].splice(i, 1); } - const newCellWidth = this._noteWidth(1 / noteValue); + let newCellWidth = this._noteWidth(1 / noteValue); // Use noteValue of downCell for REST status. if (oldNoteValue < 0) { noteValues[downCellIndex] = -1 / noteValue; @@ -1095,7 +786,7 @@ class RhythmRuler { this._mouseDownCell.style.width = newCellWidth + "px"; this._mouseDownCell.style.minWidth = this._mouseDownCell.style.width; - this._mouseDownCell.style.height = RhythmRuler.RULERHEIGHT + "px"; + this._mouseDownCell.style.height = RULERHEIGHT + "px"; this._mouseDownCell.style.minHeight = this._mouseDownCell.style.height; this._mouseDownCell.style.maxHeight = this._mouseDownCell.style.height; @@ -1107,9 +798,9 @@ class RhythmRuler { this._calculateZebraStripes(this._rulerSelected); } - } + }; - _undo() { + _undo () { // FIXME: Add undo for REST logo.synth.stop(); this._startingTime = null; @@ -1123,28 +814,28 @@ class RhythmRuler { return; } - const obj = this._undoList.pop(); - const lastRuler = obj[1]; - const divisionHistory = this.Rulers[lastRuler][1]; + let obj = this._undoList.pop(); + let lastRuler = obj[1]; + let divisionHistory = this.Rulers[lastRuler][1]; if (divisionHistory.length === 0) { return; } - const ruler = this._rulers[lastRuler]; - const noteValues = this.Rulers[lastRuler][0]; + let ruler = this._rulers[lastRuler]; + let noteValues = this.Rulers[lastRuler][0]; if (obj[0] === "dissect") { - const inputNum = divisionHistory[divisionHistory.length - 1][1]; - const newCellIndex = divisionHistory[divisionHistory.length - 1][0]; - const cellWidth = ruler.cells[newCellIndex].style.width; - const newCellWidth = parseFloat(cellWidth) * inputNum; - const oldCellNoteValue = noteValues[newCellIndex]; - const newNoteValue = oldCellNoteValue / inputNum; - - const newCell = ruler.insertCell(newCellIndex); + let inputNum = divisionHistory[divisionHistory.length - 1][1]; + let newCellIndex = divisionHistory[divisionHistory.length - 1][0]; + let cellWidth = ruler.cells[newCellIndex].style.width; + let newCellWidth = parseFloat(cellWidth) * inputNum; + let oldCellNoteValue = noteValues[newCellIndex]; + let newNoteValue = oldCellNoteValue / inputNum; + + let newCell = ruler.insertCell(newCellIndex); newCell.style.width = this._noteWidth(newNoteValue) + "px"; newCell.style.minWidth = newCell.style.width; - newCell.style.height = RhythmRuler.RULERHEIGHT + "px"; + newCell.style.height = RULERHEIGHT + "px"; newCell.style.minHeight = newCell.style.height; newCell.style.maxHeight = newCell.style.height; @@ -1164,31 +855,37 @@ class RhythmRuler { ruler.deleteCell(newCellIndex + 1); } } else if (obj[0] === "tap") { - const newCellIndex = last(divisionHistory)[0]; - const oldNoteValues = last(divisionHistory)[1]; + let newCellIndex = last(divisionHistory)[0]; + let oldNoteValues = last(divisionHistory)[1]; - // Calculate the new note value based on the sum of the oldnoteValues. + // Calculate the new note value based on the sum of the + // oldnoteValues. + let oldCellNoteValue = noteValues[newCellIndex]; let sum = 0; for (let i = 0; i < oldNoteValues.length; i++) { sum += 1 / oldNoteValues[i]; } - const newNoteValue = 1 / sum; - const newCellWidth = this._noteWidth(newNoteValue); + let newNoteValue = 1 / sum; + let newCellWidth = this._noteWidth(newNoteValue); - const newCell = ruler.insertCell(newCellIndex); + let newCell = ruler.insertCell(newCellIndex); newCell.style.width = newCellWidth + "px"; newCell.style.minWidth = newCell.style.width; - newCell.style.height = RhythmRuler.RULERHEIGHT + "px"; + newCell.style.height = RULERHEIGHT + "px"; newCell.style.minHeight = newCell.style.height; newCell.style.maxHeight = newCell.style.height; newCell.style.backgroundColor = platformColor.selectorBackground; - const obj = rationalToFraction(newNoteValue); - newCell.innerHTML = calcNoteValueToDisplay(obj[1], obj[0], this._cellScale); - - noteValues[newCellIndex] = newNoteValue; + let obj = rationalToFraction(newNoteValue); + newCell.innerHTML = calcNoteValueToDisplay( + obj[1], + obj[0], + this._cellScale + ); + + noteValues[newCellIndex] = newNoteValue; noteValues.splice(newCellIndex + 1, oldNoteValues.length - 1); this.__addCellEventHandlers(newCell, newCellWidth, newNoteValue); @@ -1197,45 +894,56 @@ class RhythmRuler { ruler.deleteCell(newCellIndex + 1); } } else if (obj[0] === "tie") { - const history = last(divisionHistory); + let history = last(divisionHistory); // The old cell is the same as the first entry in the // history. Dissect the old cell into history.length // parts and restore their size and note values. if (history.length > 0) { - const oldCell = ruler.cells[history[0][0]]; - const oldCellWidth = this._noteWidth(history[0][1]); + let oldCell = ruler.cells[history[0][0]]; + let oldCellWidth = this._noteWidth(history[0][1]); oldCell.style.width = oldCellWidth + "px"; oldCell.style.minWidth = oldCell.style.width; - oldCell.style.height = RhythmRuler.RULERHEIGHT + "px"; + oldCell.style.height = RULERHEIGHT + "px"; oldCell.style.minHeight = oldCell.style.height; oldCell.style.maxHeight = oldCell.style.height; noteValues[history[0][0]] = history[0][1]; - this.__addCellEventHandlers(oldCell, oldCellWidth, history[0][1]); + this.__addCellEventHandlers( + oldCell, + oldCellWidth, + history[0][1] + ); for (let i = 1; i < history.length; i++) { - const newCell = ruler.insertCell(history[0][0] + i); - const newCellWidth = this._noteWidth(history[i][1]); + let newCell = ruler.insertCell(history[0][0] + i); + let newCellWidth = this._noteWidth(history[i][1]); newCell.style.width = newCellWidth + "px"; newCell.style.minWidth = newCell.style.width; - newCell.style.height = RhythmRuler.RULERHEIGHT + "px"; + newCell.style.height = RULERHEIGHT + "px"; newCell.style.minHeight = newCell.style.height; newCell.style.maxHeight = newCell.style.height; noteValues.splice(history[0][0] + i, 0, history[i][1]); - newCell.innerHTML = calcNoteValueToDisplay(history[i][1], 1, this._cellScale); + newCell.innerHTML = calcNoteValueToDisplay( + history[i][1], + 1, + this._cellScale + ); - this.__addCellEventHandlers(newCell, newCellWidth, history[i][1]); + this.__addCellEventHandlers( + newCell, + newCellWidth, + history[i][1] + ); } this.Rulers[lastRuler][0] = noteValues; + } else { + console.warn("empty history encountered... skipping undo"); } - // else { - // console.warn("empty history encountered... skipping undo"); - // } } else if (obj[0] === "rest") { - const newCellIndex = last(divisionHistory); - const cell = ruler.cells[newCellIndex]; + let newCellIndex = last(divisionHistory); + let cell = ruler.cells[newCellIndex]; this.__toggleRestState(cell, false); divisionHistory.pop(); } @@ -1244,23 +952,24 @@ class RhythmRuler { this._calculateZebraStripes(lastRuler); // this._piemenuRuler(this._rulerSelected); - } + }; - _tap() { + _tap () { this._tapMode = true; + let iconSize = ICONSIZE; this._tapButton.innerHTML = '' +
             _('; - } + }; - _clear() { + _clear () { logo.synth.stop(); logo.resetSynth(0); this._playing = false; @@ -1268,17 +977,18 @@ class RhythmRuler { this._playingOne = false; this._rulerPlaying = -1; this._startingTime = null; + let iconSize = ICONSIZE; this._playAllCell.innerHTML = '' +
             _('; - for (let r = 0; r < this.Rulers.length; r++) { + for (r = 0; r < this.Rulers.length; r++) { this._rulerSelected = r; while (this.Rulers[r][1].length > 0) { this._undo(); @@ -1286,18 +996,19 @@ class RhythmRuler { } // this._piemenuRuler(this._rulerSelected); - } + }; - __pause() { + __pause () { + let iconSize = ICONSIZE; this._playAllCell.innerHTML = '' +
             _('; this._playing = false; this._playingAll = false; @@ -1307,34 +1018,36 @@ class RhythmRuler { for (let i = 0; i < this.Rulers.length; i++) { this._calculateZebraStripes(i); } - } + }; - playAll() { + playAll () { // External call from run button. if (this._playing) { if (this._playingAll) { this.__pause(); // Wait for pause to complete before restarting. this._playingAll = true; - setTimeout(() => { + + setTimeout(function () { this.__resume(); }, 1000); } } else if (!this._playingAll) { this.__resume(); } - } + }; - __resume() { + __resume () { + let iconSize = ICONSIZE; this._playAllCell.innerHTML = '' +
             _('; logo.turtleDelay = 0; this._playingAll = true; @@ -1348,13 +1061,13 @@ class RhythmRuler { } this._playAll(); - } + }; - _playAll() { + _playAll () { logo.synth.stop(); logo.resetSynth(0); if (this._startingTime === null) { - const d = new Date(); + let d = new Date(); this._startingTime = d.getTime(); for (let i = 0; i < this.Rulers.length; i++) { this._offsets[i] = 0; @@ -1365,26 +1078,26 @@ class RhythmRuler { for (let i = 0; i < this.Rulers.length; i++) { this.__loop(0, i, 0); } - } + }; - _playOne() { + _playOne () { logo.synth.stop(); logo.resetSynth(0); if (this._startingTime === null) { - const d = new Date(); + let d = new Date(); this._startingTime = d.getTime(); this._elapsedTimes[this._rulerSelected] = 0; this._offsets[this._rulerSelected] = 0; } - // console.debug("this._rulerSelected " + this._rulerSelected); + console.debug("this._rulerSelected " + this._rulerSelected); this.__loop(0, this._rulerSelected, 0); - } + }; - __loop(noteTime, rulerNo, colIndex) { - const ruler = this._rulers[rulerNo]; + __loop (noteTime, rulerNo, colIndex) { + let ruler = this._rulers[rulerNo]; if (ruler === null) { - // console.warn("Cannot find ruler " + rulerNo + ". Widget closed?"); + console.warn("Cannot find ruler " + rulerNo + ". Widget closed?"); return; } @@ -1393,16 +1106,17 @@ class RhythmRuler { this._calculateZebraStripes(rulerNo); } - const cell = ruler.cells[colIndex]; - const noteValues = this.Rulers[rulerNo][0]; - const noteValue = noteValues[colIndex]; + let cell = ruler.cells[colIndex]; + let noteValues = this.Rulers[rulerNo][0]; + let noteValue = noteValues[colIndex]; noteTime = Math.abs(1 / noteValue); let drum; if (this.Drums[rulerNo] === null) { drum = "snare drum"; } else { - const drumblockno = logo.blocks.blockList[this.Drums[rulerNo]].connections[1]; + let drumblockno = logo.blocks.blockList[this.Drums[rulerNo]] + .connections[1]; drum = logo.blocks.blockList[drumblockno].value; } @@ -1433,27 +1147,18 @@ class RhythmRuler { } } + + if (this._playing) { // Play the current note. if (noteValue > 0) { if (foundVoice) { logo.synth.trigger( - 0, - "C4", - Singer.defaultBPMFactor / noteValue, - drum, - null, - null, - false + 0, "C4", Singer.defaultBPMFactor / noteValue, drum, null, null, false ); } else if (foundDrum) { logo.synth.trigger( - 0, - ["C4"], - Singer.defaultBPMFactor / noteValue, - drum, - null, - null + 0, ["C4"], Singer.defaultBPMFactor / noteValue, drum, null, null ); } } @@ -1462,11 +1167,12 @@ class RhythmRuler { cell.style.backgroundColor = platformColor.rulerHighlight; // selectorBackground; // Calculate any offset in playback. - const d = new Date(); - this._offsets[rulerNo] = d.getTime() - this._startingTime - this._elapsedTimes[rulerNo]; + let d = new Date(); + this._offsets[rulerNo] = + d.getTime() - this._startingTime - this._elapsedTimes[rulerNo]; } - setTimeout(() => { + setTimeout(function () { colIndex += 1; if (colIndex === noteValues.length) { colIndex = 0; @@ -1478,19 +1184,20 @@ class RhythmRuler { }, Singer.defaultBPMFactor * 1000 * noteTime - this._offsets[rulerNo]); this._elapsedTimes[rulerNo] += Singer.defaultBPMFactor * 1000 * noteTime; - } + }; - _save(selectedRuler) { + _save (selectedRuler) { // Deprecated -- replaced by save tuplets code - for (const name in logo.blocks.palettes.dict) { + + for (let name in logo.blocks.palettes.dict) { logo.blocks.palettes.dict[name].hideMenu(true); } logo.refreshCanvas(); - setTimeout(() => { - const ruler = this._rulers[selectedRuler]; - const noteValues = this.Rulers[selectedRuler][0]; + setTimeout(function () { + let ruler = this._rulers[selectedRuler]; + let noteValues = this.Rulers[selectedRuler][0]; // Get the first word of drum's name (ignore the word 'drum' itself) // and add 'rhythm'. let stack_value; @@ -1499,49 +1206,37 @@ class RhythmRuler { } else { stack_value = logo.blocks.blockList[ - logo.blocks.blockList[this.Drums[selectedRuler]].connections[1] + logo.blocks.blockList[this.Drums[selectedRuler]] + .connections[1] ].value.split(" ")[0] + " " + _("rhythm"); } - const delta = selectedRuler * 42; - const newStack = [ + let delta = selectedRuler * 42; + let newStack = [ [ 0, - [ - "action", - { - collapsed: true - } - ], + ["action", { collapsed: true }], 100 + delta, 100 + delta, [null, 1, 2, null] ], - [ - 1, - [ - "text", - { - value: stack_value - } - ], - 0, - 0, - [0] - ] + [1, ["text", { value: stack_value }], 0, 0, [0]] ]; let previousBlock = 0; let sameNoteValue = 1; for (let i = 0; i < ruler.cells.length; i++) { - if (noteValues[i] === noteValues[i + 1] && i < ruler.cells.length - 1) { + if ( + noteValues[i] === noteValues[i + 1] && + i < ruler.cells.length - 1 + ) { sameNoteValue += 1; continue; } else { - const idx = newStack.length; - const noteValue = noteValues[i]; + let idx = newStack.length; + let noteValue = noteValues[i]; - const obj = rationalToFraction(1 / Math.abs(noteValue)); + let obj = rationalToFraction(1 / Math.abs(noteValue)); newStack.push([ idx, @@ -1552,46 +1247,49 @@ class RhythmRuler { ]); newStack.push([ idx + 1, - [ - "number", - { - value: sameNoteValue - } - ], + ["number", { value: sameNoteValue }], 0, 0, [idx] ]); - newStack.push([idx + 2, "divide", 0, 0, [idx, idx + 3, idx + 4]]); + newStack.push([ + idx + 2, + "divide", + 0, + 0, + [idx, idx + 3, idx + 4] + ]); newStack.push([ idx + 3, - [ - "number", - { - value: obj[0] - } - ], + ["number", { value: obj[0] }], 0, 0, [idx + 2] ]); newStack.push([ idx + 4, - [ - "number", - { - value: obj[1] - } - ], + ["number", { value: obj[1] }], 0, 0, [idx + 2] ]); newStack.push([idx + 5, "vspace", 0, 0, [idx, idx + 6]]); if (i == ruler.cells.length - 1) { - newStack.push([idx + 6, "hidden", 0, 0, [idx + 5, null]]); + newStack.push([ + idx + 6, + "hidden", + 0, + 0, + [idx + 5, null] + ]); } else { - newStack.push([idx + 6, "hidden", 0, 0, [idx + 5, idx + 7]]); + newStack.push([ + idx + 6, + "hidden", + 0, + 0, + [idx + 5, idx + 7] + ]); } previousBlock = idx + 6; @@ -1606,67 +1304,56 @@ class RhythmRuler { this._save(selectedRuler + 1); } }, 500); - } + }; - _saveTuplets(selectedRuler) { - for (const name in logo.blocks.palettes.dict) { + _saveTuplets (selectedRuler) { + + for (let name in logo.blocks.palettes.dict) { logo.blocks.palettes.dict[name].hideMenu(true); } logo.refreshCanvas(); - setTimeout(() => { - const ruler = this._rulers[selectedRuler]; - const noteValues = this.Rulers[selectedRuler][0]; + setTimeout(function () { + let ruler = this._rulers[selectedRuler]; + let noteValues = this.Rulers[selectedRuler][0]; let stack_value; if (this.Drums[selectedRuler] === null) { stack_value = _("rhythm"); } else { stack_value = logo.blocks.blockList[ - logo.blocks.blockList[this.Drums[selectedRuler]].connections[1] + logo.blocks.blockList[this.Drums[selectedRuler]] + .connections[1] ].value.split(" ")[0] + " " + _("rhythm"); } - const delta = selectedRuler * 42; - const newStack = [ + let delta = selectedRuler * 42; + let newStack = [ [ 0, - [ - "action", - { - collapsed: true - } - ], + ["action", { collapsed: true }], 100 + delta, 100 + delta, [null, 1, 2, null] ], - [ - 1, - [ - "text", - { - value: stack_value - } - ], - 0, - 0, - [0] - ] + [1, ["text", { value: stack_value }], 0, 0, [0]] ]; let previousBlock = 0; let sameNoteValue = 1; for (let i = 0; i < ruler.cells.length; i++) { - if (noteValues[i] === noteValues[i + 1] && i < ruler.cells.length - 1) { + if ( + noteValues[i] === noteValues[i + 1] && + i < ruler.cells.length - 1 + ) { sameNoteValue += 1; continue; } else { - const idx = newStack.length; - const noteValue = noteValues[i]; - const obj = rationalToFraction(1 / Math.abs(noteValue)); - const n = obj[1] / sameNoteValue; + let idx = newStack.length; + let noteValue = noteValues[i]; + let obj = rationalToFraction(1 / Math.abs(noteValue)); + let n = obj[1] / sameNoteValue; if (Number.isInteger(n)) { newStack.push([ idx, @@ -1677,42 +1364,39 @@ class RhythmRuler { ]); newStack.push([ idx + 1, - [ - "number", - { - value: sameNoteValue - } - ], + ["number", { value: sameNoteValue }], 0, 0, [idx] ]); - newStack.push([idx + 2, "divide", 0, 0, [idx, idx + 3, idx + 4]]); + newStack.push([ + idx + 2, + "divide", + 0, + 0, + [idx, idx + 3, idx + 4] + ]); newStack.push([ idx + 3, - [ - "number", - { - value: obj[0] - } - ], + ["number", { value: obj[0] }], 0, 0, [idx + 2] ]); newStack.push([ idx + 4, - [ - "number", - { - value: n - } - ], + ["number", { value: n }], 0, 0, [idx + 2] ]); - newStack.push([idx + 5, "vspace", 0, 0, [idx, idx + 6]]); + newStack.push([ + idx + 5, + "vspace", + 0, + 0, + [idx, idx + 6] + ]); } else { newStack.push([ idx, @@ -1723,48 +1407,57 @@ class RhythmRuler { ]); newStack.push([ idx + 1, - [ - "number", - { - value: sameNoteValue - } - ], + ["number", { value: sameNoteValue }], 0, 0, [idx] ]); - newStack.push([idx + 2, "divide", 0, 0, [idx, idx + 3, idx + 4]]); + newStack.push([ + idx + 2, + "divide", + 0, + 0, + [idx, idx + 3, idx + 4] + ]); newStack.push([ idx + 3, - [ - "number", - { - value: obj[0] - } - ], + ["number", { value: obj[0] }], 0, 0, [idx + 2] ]); newStack.push([ idx + 4, - [ - "number", - { - value: obj[1] - } - ], + ["number", { value: obj[1] }], 0, 0, [idx + 2] ]); - newStack.push([idx + 5, "vspace", 0, 0, [idx, idx + 6]]); + newStack.push([ + idx + 5, + "vspace", + 0, + 0, + [idx, idx + 6] + ]); } if (i == ruler.cells.length - 1) { - newStack.push([idx + 6, "hidden", 0, 0, [idx + 5, null]]); + newStack.push([ + idx + 6, + "hidden", + 0, + 0, + [idx + 5, null] + ]); } else { - newStack.push([idx + 6, "hidden", 0, 0, [idx + 5, idx + 7]]); + newStack.push([ + idx + 6, + "hidden", + 0, + 0, + [idx + 5, idx + 7] + ]); } previousBlock = idx + 6; @@ -1779,87 +1472,72 @@ class RhythmRuler { this._saveTuplets(selectedRuler + 1); } }, 500); - } + }; - _saveTupletsMerged(noteValues) { - for (const name in logo.blocks.palettes.dict) { + _saveTupletsMerged (noteValues) { + + for (let name in logo.blocks.palettes.dict) { logo.blocks.palettes.dict[name].hideMenu(true); } logo.refreshCanvas(); - const stack_value = _("rhythm"); - const delta = 42; - const newStack = [ + let stack_value = _("rhythm"); + let delta = 42; + let newStack = [ [ 0, - [ - "action", - { - collapsed: true - } - ], + ["action", { collapsed: true }], 100 + delta, 100 + delta, [null, 1, 2, null] ], - [ - 1, - [ - "text", - { - value: stack_value - } - ], - 0, - 0, - [0] - ] + [1, ["text", { value: stack_value }], 0, 0, [0]] ]; let previousBlock = 0; let sameNoteValue = 1; for (let i = 0; i < noteValues.length; i++) { - if (noteValues[i] === noteValues[i + 1] && i < noteValues.length - 1) { + if ( + noteValues[i] === noteValues[i + 1] && + i < noteValues.length - 1 + ) { sameNoteValue += 1; continue; } else { - const idx = newStack.length; - const noteValue = noteValues[i]; - const obj = rationalToFraction(1 / Math.abs(noteValue)); - newStack.push([idx, "rhythm2", 0, 0, [previousBlock, idx + 1, idx + 2, idx + 5]]); + let idx = newStack.length; + let noteValue = noteValues[i]; + let obj = rationalToFraction(1 / Math.abs(noteValue)); + newStack.push([ + idx, + "rhythm2", + 0, + 0, + [previousBlock, idx + 1, idx + 2, idx + 5] + ]); newStack.push([ idx + 1, - [ - "number", - { - value: sameNoteValue - } - ], + ["number", { value: sameNoteValue }], 0, 0, [idx] ]); - newStack.push([idx + 2, "divide", 0, 0, [idx, idx + 3, idx + 4]]); + newStack.push([ + idx + 2, + "divide", + 0, + 0, + [idx, idx + 3, idx + 4] + ]); newStack.push([ idx + 3, - [ - "number", - { - value: obj[0] - } - ], + ["number", { value: obj[0] }], 0, 0, [idx + 2] ]); newStack.push([ idx + 4, - [ - "number", - { - value: obj[1] - } - ], + ["number", { value: obj[1] }], 0, 0, [idx + 2] @@ -1869,7 +1547,13 @@ class RhythmRuler { if (i == noteValues.length - 1) { newStack.push([idx + 6, "hidden", 0, 0, [idx + 5, null]]); } else { - newStack.push([idx + 6, "hidden", 0, 0, [idx + 5, idx + 7]]); + newStack.push([ + idx + 6, + "hidden", + 0, + 0, + [idx + 5, idx + 7] + ]); } previousBlock = idx + 6; @@ -1879,15 +1563,17 @@ class RhythmRuler { logo.blocks.loadNewBlocks(newStack); logo.textMsg(_("New action block generated!")); - } + }; - _saveMachine(selectedRuler) { + _saveMachine (selectedRuler) { // We are either saving a drum machine or a voice machine. let drum; if (this.Drums[selectedRuler] === null) { drum = "snare drum"; } else { - const drumBlockNo = logo.blocks.blockList[this.Drums[selectedRuler]].connections[1]; + let drumBlockNo = logo.blocks.blockList[ + this.Drums[selectedRuler] + ].connections[1]; drum = logo.blocks.blockList[drumBlockNo].value; } @@ -1909,19 +1595,20 @@ class RhythmRuler { return; } } - } + }; - _saveDrumMachine(selectedRuler, drum, effect) { - for (const name in logo.blocks.palettes.dict) { + _saveDrumMachine (selectedRuler, drum, effect) { + + for (let name in logo.blocks.palettes.dict) { logo.blocks.palettes.dict[name].hideMenu(true); } logo.refreshCanvas(); - setTimeout(() => { - const ruler = this._rulers[selectedRuler]; - const noteValues = this.Rulers[selectedRuler][0]; - const delta = selectedRuler * 42; + setTimeout(function () { + let ruler = this._rulers[selectedRuler]; + let noteValues = this.Rulers[selectedRuler][0]; + let delta = selectedRuler * 42; // Just save the action, not the drum machine itself. // let newStack = [[0, ['start', {'collapsed': false}], 100 + delta, 100 + delta, [null, 1, null]]]; @@ -1932,49 +1619,37 @@ class RhythmRuler { } else { action_name = logo.blocks.blockList[ - logo.blocks.blockList[this.Drums[selectedRuler]].connections[1] + logo.blocks.blockList[this.Drums[selectedRuler]] + .connections[1] ].value.split(" ")[0] + " " + _("action"); } - const newStack = [ + let newStack = [ [ 0, - [ - "action", - { - collapsed: true - } - ], + ["action", { collapsed: true }], 100 + delta, 100 + delta, [null, 1, 2, null] ], - [ - 1, - [ - "text", - { - value: action_name - } - ], - 0, - 0, - [0] - ] + [1, ["text", { value: action_name }], 0, 0, [0]] ]; let previousBlock = 0; // 1 let sameNoteValue = 1; for (let i = 0; i < ruler.cells.length; i++) { - if (noteValues[i] === noteValues[i + 1] && i < ruler.cells.length - 1) { + if ( + noteValues[i] === noteValues[i + 1] && + i < ruler.cells.length - 1 + ) { sameNoteValue += 1; continue; } else { - const idx = newStack.length; - const noteValue = noteValues[i]; + let idx = newStack.length; + let noteValue = noteValues[i]; - const obj = rationalToFraction(1 / Math.abs(noteValue)); + let obj = rationalToFraction(1 / Math.abs(noteValue)); if (sameNoteValue === 1) { // Add a note block. @@ -1985,15 +1660,16 @@ class RhythmRuler { 0, [previousBlock, idx + 1, idx + 4, idx + 7] ]); - newStack.push([idx + 1, "divide", 0, 0, [idx, idx + 2, idx + 3]]); + newStack.push([ + idx + 1, + "divide", + 0, + 0, + [idx, idx + 2, idx + 3] + ]); newStack.push([ idx + 2, - [ - "number", - { - value: obj[0] - } - ], + ["number", { value: obj[0] }], 0, 0, [idx + 1] @@ -2001,43 +1677,58 @@ class RhythmRuler { if (noteValue < 0) { newStack.push([ idx + 3, - [ - "number", - { - value: obj[1] - } - ], + ["number", { value: obj[1] }], 0, 0, [idx + 1] ]); - newStack.push([idx + 4, "vspace", 0, 0, [idx, idx + 5]]); - newStack.push([idx + 5, "rest2", 0, 0, [idx + 4, idx + 6]]); - newStack.push([idx + 6, "hidden", 0, 0, [idx + 5, null]]); + newStack.push([ + idx + 4, + "vspace", + 0, + 0, + [idx, idx + 5] + ]); + newStack.push([ + idx + 5, + "rest2", + 0, + 0, + [idx + 4, idx + 6] + ]); + newStack.push([ + idx + 6, + "hidden", + 0, + 0, + [idx + 5, null] + ]); } else { newStack.push([ idx + 3, - [ - "number", - { - value: obj[1] - } - ], + ["number", { value: obj[1] }], 0, 0, [idx + 1] ]); - newStack.push([idx + 4, "vspace", 0, 0, [idx, idx + 5]]); - newStack.push([idx + 5, "playdrum", 0, 0, [idx + 4, idx + 6, null]]); + newStack.push([ + idx + 4, + "vspace", + 0, + 0, + [idx, idx + 5] + ]); + newStack.push([ + idx + 5, + "playdrum", + 0, + 0, + [idx + 4, idx + 6, null] + ]); if (effect) { newStack.push([ idx + 6, - [ - "effectsname", - { - value: drum - } - ], + ["effectsname", { value: drum }], 0, 0, [idx + 5] @@ -2045,12 +1736,7 @@ class RhythmRuler { } else { newStack.push([ idx + 6, - [ - "drumname", - { - value: drum - } - ], + ["drumname", { value: drum }], 0, 0, [idx + 5] @@ -2058,9 +1744,21 @@ class RhythmRuler { } } if (i == ruler.cells.length - 1) { - newStack.push([idx + 7, "hidden", 0, 0, [idx, null]]); + newStack.push([ + idx + 7, + "hidden", + 0, + 0, + [idx, null] + ]); } else { - newStack.push([idx + 7, "hidden", 0, 0, [idx, idx + 8]]); + newStack.push([ + idx + 7, + "hidden", + 0, + 0, + [idx, idx + 8] + ]); previousBlock = idx + 7; } } else { @@ -2085,26 +1783,28 @@ class RhythmRuler { } newStack.push([ idx + 1, - [ - "number", - { - value: sameNoteValue - } - ], + ["number", { value: sameNoteValue }], 0, 0, [idx] ]); - newStack.push([idx + 2, "newnote", 0, 0, [idx, idx + 3, idx + 6, idx + 9]]); - newStack.push([idx + 3, "divide", 0, 0, [idx + 2, idx + 4, idx + 5]]); + newStack.push([ + idx + 2, + "newnote", + 0, + 0, + [idx, idx + 3, idx + 6, idx + 9] + ]); + newStack.push([ + idx + 3, + "divide", + 0, + 0, + [idx + 2, idx + 4, idx + 5] + ]); newStack.push([ idx + 4, - [ - "number", - { - value: 1 - } - ], + ["number", { value: 1 }], 0, 0, [idx + 3] @@ -2112,43 +1812,58 @@ class RhythmRuler { if (noteValue < 0) { newStack.push([ idx + 5, - [ - "number", - { - value: -noteValue - } - ], + ["number", { value: -noteValue }], 0, 0, [idx + 3] ]); - newStack.push([idx + 6, "vspace", 0, 0, [idx + 2, idx + 7]]); - newStack.push([idx + 7, "rest2", 0, 0, [idx + 6, idx + 8]]); - newStack.push([idx + 8, "hidden", 0, 0, [idx + 7, null]]); + newStack.push([ + idx + 6, + "vspace", + 0, + 0, + [idx + 2, idx + 7] + ]); + newStack.push([ + idx + 7, + "rest2", + 0, + 0, + [idx + 6, idx + 8] + ]); + newStack.push([ + idx + 8, + "hidden", + 0, + 0, + [idx + 7, null] + ]); } else { newStack.push([ idx + 5, - [ - "number", - { - value: noteValue - } - ], + ["number", { value: noteValue }], 0, 0, [idx + 3] ]); - newStack.push([idx + 6, "vspace", 0, 0, [idx + 2, idx + 7]]); - newStack.push([idx + 7, "playdrum", 0, 0, [idx + 6, idx + 8, null]]); + newStack.push([ + idx + 6, + "vspace", + 0, + 0, + [idx + 2, idx + 7] + ]); + newStack.push([ + idx + 7, + "playdrum", + 0, + 0, + [idx + 6, idx + 8, null] + ]); if (effect) { newStack.push([ idx + 8, - [ - "effectsname", - { - value: drum - } - ], + ["effectsname", { value: drum }], 0, 0, [idx + 7] @@ -2156,19 +1871,20 @@ class RhythmRuler { } else { newStack.push([ idx + 8, - [ - "drumname", - { - value: drum - } - ], + ["drumname", { value: drum }], 0, 0, [idx + 7] ]); } } - newStack.push([idx + 9, "hidden", 0, 0, [idx + 2, null]]); + newStack.push([ + idx + 9, + "hidden", + 0, + 0, + [idx + 2, null] + ]); } sameNoteValue = 1; @@ -2183,19 +1899,20 @@ class RhythmRuler { this._saveMachine(selectedRuler + 1); } }, 500); - } + }; - _saveVoiceMachine(selectedRuler, voice) { - for (const name in logo.blocks.palettes.dict) { + _saveVoiceMachine (selectedRuler, voice) { + + for (let name in logo.blocks.palettes.dict) { logo.blocks.palettes.dict[name].hideMenu(true); } logo.refreshCanvas(); - setTimeout(() => { - const ruler = this._rulers[selectedRuler]; - const noteValues = this.Rulers[selectedRuler][0]; - const delta = selectedRuler * 42; + setTimeout(function () { + let ruler = this._rulers[selectedRuler]; + let noteValues = this.Rulers[selectedRuler][0]; + let delta = selectedRuler * 42; // Just save the action, not the drum machine itself. // let newStack = [[0, ['start', {'collapsed': false}], 100 + delta, 100 + delta, [null, 1, null]]]; @@ -2208,375 +1925,828 @@ class RhythmRuler { // This should never happen. let action_name; if (this.Drums[selectedRuler] === null) { - action_name = _("guitar") + " " + _("action"); + let action_name = _("guitar") + " " + _("action"); } else { - // const action_name = - // logo.blocks.blockList[ - // logo.blocks.blockList[this.Drums[selectedRuler]].connections[1] - // ].value.split(" ")[0] + - // "_" + - // _("action"); + let action_name = + logo.blocks.blockList[ + logo.blocks.blockList[this.Drums[selectedRuler]] + .connections[1] + ].value.split(" ")[0] + + "_" + + _("action"); + } + + let newStack = [ + [ + 0, + ["action", { collapsed: true }], + 100 + delta, + 100 + delta, + [null, 1, 2, null] + ], + [1, ["text", { value: action_name }], 0, 0, [0]] + ]; + newStack.push([2, "settimbre", 0, 0, [0, 3, 5, 4]]); + newStack.push([3, ["voicename", { value: voice }], 0, 0, [2]]); + newStack.push([4, "hidden", 0, 0, [2, null]]); + let previousBlock = 2; + let sameNoteValue = 1; + for (let i = 0; i < ruler.cells.length; i++) { + if ( + noteValues[i] === noteValues[i + 1] && + i < ruler.cells.length - 1 + ) { + sameNoteValue += 1; + continue; + } else { + let idx = newStack.length; + let noteValue = noteValues[i]; + + let obj = rationalToFraction(1 / Math.abs(noteValue)); + + if (sameNoteValue === 1) { + // Add a note block. + if (noteValue < 0) { + newStack.push([ + idx, + "newnote", + 0, + 0, + [previousBlock, idx + 1, idx + 4, idx + 7] + ]); + newStack.push([ + idx + 1, + "divide", + 0, + 0, + [idx, idx + 2, idx + 3] + ]); + newStack.push([ + idx + 2, + ["number", { value: obj[0] }], + 0, + 0, + [idx + 1] + ]); + newStack.push([ + idx + 3, + ["number", { value: obj[1] }], + 0, + 0, + [idx + 1] + ]); + newStack.push([ + idx + 4, + "vspace", + 0, + 0, + [idx, idx + 5] + ]); + newStack.push([ + idx + 5, + "rest2", + 0, + 0, + [idx + 4, idx + 6] + ]); + newStack.push([ + idx + 6, + "hidden", + 0, + 0, + [idx + 5, null] + ]); + if (i == ruler.cells.length - 1) { + newStack.push([ + idx + 7, + "hidden", + 0, + 0, + [idx, null] + ]); + } else { + newStack.push([ + idx + 7, + "hidden", + 0, + 0, + [idx, idx + 8] + ]); + previousBlock = idx + 7; + } + } else { + newStack.push([ + idx, + "newnote", + 0, + 0, + [previousBlock, idx + 1, idx + 4, idx + 8] + ]); + newStack.push([ + idx + 1, + "divide", + 0, + 0, + [idx, idx + 2, idx + 3] + ]); + newStack.push([ + idx + 2, + ["number", { value: obj[0] }], + 0, + 0, + [idx + 1] + ]); + newStack.push([ + idx + 3, + ["number", { value: obj[1] }], + 0, + 0, + [idx + 1] + ]); + newStack.push([ + idx + 4, + "vspace", + 0, + 0, + [idx, idx + 5] + ]); + newStack.push([ + idx + 5, + "pitch", + 0, + 0, + [idx + 4, idx + 6, idx + 7, null] + ]); + newStack.push([ + idx + 6, + ["notename", { value: "C" }], + 0, + 0, + [idx + 5] + ]); + newStack.push([ + idx + 7, + ["number", { value: 4 }], + 0, + 0, + [idx + 5] + ]); + if (i == ruler.cells.length - 1) { + newStack.push([ + idx + 8, + "hidden", + 0, + 0, + [idx, null] + ]); + } else { + newStack.push([ + idx + 8, + "hidden", + 0, + 0, + [idx, idx + 9] + ]); + previousBlock = idx + 8; + } + } + } else { + // Add a note block inside a repeat block. + if (i == ruler.cells.length - 1) { + newStack.push([ + idx, + "repeat", + 0, + 0, + [previousBlock, idx + 1, idx + 2, null] + ]); + } else { + newStack.push([ + idx, + "repeat", + 0, + 0, + [previousBlock, idx + 1, idx + 2, idx + 11] + ]); + previousBlock = idx; + } + newStack.push([ + idx + 1, + ["number", { value: sameNoteValue }], + 0, + 0, + [idx] + ]); + if (noteValue < 0) { + newStack.push([ + idx + 2, + "newnote", + 0, + 0, + [idx, idx + 3, idx + 6, idx + 9] + ]); + newStack.push([ + idx + 3, + "divide", + 0, + 0, + [idx + 2, idx + 4, idx + 5] + ]); + newStack.push([ + idx + 4, + ["number", { value: 1 }], + 0, + 0, + [idx + 3] + ]); + newStack.push([ + idx + 5, + ["number", { value: -noteValue }], + 0, + 0, + [idx + 3] + ]); + newStack.push([ + idx + 6, + "vspace", + 0, + 0, + [idx + 2, idx + 7] + ]); + newStack.push([ + idx + 7, + "rest2", + 0, + 0, + [idx + 6, idx + 8] + ]); + newStack.push([ + idx + 8, + "hidden", + 0, + 0, + [idx + 7, null] + ]); + newStack.push([ + idx + 9, + "hidden", + 0, + 0, + [idx + 2, null] + ]); + } else { + newStack.push([ + idx + 2, + "newnote", + 0, + 0, + [idx, idx + 3, idx + 6, idx + 10] + ]); + newStack.push([ + idx + 3, + "divide", + 0, + 0, + [idx + 2, idx + 4, idx + 5] + ]); + newStack.push([ + idx + 4, + ["number", { value: 1 }], + 0, + 0, + [idx + 3] + ]); + newStack.push([ + idx + 5, + ["number", { value: noteValue }], + 0, + 0, + [idx + 3] + ]); + newStack.push([ + idx + 6, + "vspace", + 0, + 0, + [idx + 2, idx + 7] + ]); + newStack.push([ + idx + 7, + "pitch", + 0, + 0, + [idx + 6, idx + 8, idx + 9, null] + ]); + newStack.push([ + idx + 8, + ["notename", { value: "C" }], + 0, + 0, + [idx + 7] + ]); + newStack.push([ + idx + 9, + ["number", { value: 4 }], + 0, + 0, + [idx + 7] + ]); + newStack.push([ + idx + 10, + "hidden", + 0, + 0, + [idx + 2, null] + ]); + } + } + + sameNoteValue = 1; + } + } + + logo.blocks.loadNewBlocks(newStack); + if (selectedRuler > this.Rulers.length - 2) { + return; + } else { + this._saveMachine(selectedRuler + 1); + } + }, 500); + }; + + _mergeRulers () { + // Merge the rulers into one set of rhythms. + rList = []; + let noteValues; + for (let r = 0; r < this.Rulers.length; r++) { + let t = 0; + let selectedRuler = this.Rulers[r]; + noteValues = selectedRuler[0]; + for (let i = 0; i < noteValues.length; i++) { + t += 1 / noteValues[i]; + if (rList.indexOf(t) === -1) { + rList.push(t); + } + } + } + + rList.sort(function (a, b) { + return a - b; + }); + + noteValues = []; + for (let i = 0; i < rList.length; i++) { + if (i === 0) { + noteValues.push(1 / rList[i]); + } else { + noteValues.push(1 / (rList[i] - rList[i - 1])); + } + } + + return noteValues; + }; + + _get_save_lock () { + return this._save_lock; + }; + + init () { + // console.debug("init RhythmRuler"); + + this._bpmFactor = (1000 * TONEBPM) / Singer.masterBPM; + + this._playing = false; + this._playingOne = false; + this._playingAll = false; + this._rulerPlaying = -1; + this._startingTime = null; + this._expanded = false; + + // If there are no drums, add one. + if (this.Drums.length === 0) { + this.Drums.push(null); + this.Rulers.push([[1], []]); + } + + this._elapsedTimes = []; + this._offsets = []; + for (let i = 0; i < this.Rulers.length; i++) { + this._elapsedTimes.push(0); + this._offsets.push(0); + } + + let w = window.innerWidth; + this._cellScale = 1.0; + let iconSize = ICONSIZE; + + let widgetWindow = window.widgetWindows.windowFor(this, "rhythm maker"); + this.widgetWindow = widgetWindow; + widgetWindow.clear(); + widgetWindow.show(); + + // For the button callbacks + + + widgetWindow.onclose = function () { + // If the piemenu was open, close it. + // docById('wheelDiv').style.display = 'none'; + // docById('contextWheelDiv').style.display = 'none'; + + // Save the new dissect history. + let dissectHistory = []; + let drums = []; + for (let i = 0; i < this.Rulers.length; i++) { + if (this.Drums[i] === null) { + continue; + } + + let history = []; + for (let j = 0; j < this.Rulers[i][1].length; j++) { + history.push(this.Rulers[i][1][j]); + } + + this._dissectNumber.classList.add("hasKeyboard"); + dissectHistory.push([history, this.Drums[i]]); + drums.push(this.Drums[i]); + } + + // Look for any old entries that we may have missed. + for (let i = 0; i < this._dissectHistory.length; i++) { + let drum = this._dissectHistory[i][1]; + if (drums.indexOf(drum) === -1) { + let history = JSON.parse( + JSON.stringify(this._dissectHistory[i][0]) + ); + dissectHistory.push([history, drum]); + } + } + + this._dissectHistory = JSON.parse(JSON.stringify(dissectHistory)); + + this._playing = false; + this._playingOne = false; + this._playingAll = false; + logo.hideMsgs(); + + this.widgetWindow.destroy(); + }; + + this._playAllCell = widgetWindow.addButton( + "play-button.svg", + iconSize, + _("Play all") + ); + this._playAllCell.onclick = () => { + if (this._playing) { + this.__pause(); + } else if (!this._playingAll) { + this.__resume(); + } + }; + + this._save_lock = false; + widgetWindow.addButton( + "export-chunk.svg", + iconSize, + _("Save rhythms") + ).onclick = async function () { + // this._save(0); + // Debounce button + if (!this._get_save_lock()) { + this._save_lock = true; + + // Save a merged version of the rulers. + this._saveTupletsMerged(this._mergeRulers()); + + // Rather than each ruler individually. + // this._saveTuplets(0); + await delayExecution(1000); + this._save_lock = false; + } + }; + + widgetWindow.addButton( + "export-drums.svg", + iconSize, + _("Save drum machine") + ).onclick = async function () { + // Debounce button + if (!this._get_save_lock()) { + this._save_lock = true; + this._saveMachine(0); + await delayExecution(1000); + this._save_lock = false; + } + }; + + // An input for setting the dissect number + this._dissectNumber = widgetWindow.addInputButton("2"); + + this._dissectNumber.onfocus = (event) => { + // this._piemenuNumber(['2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16'], numberInput.value); + }; + + this._dissectNumber.onkeydown = (event) => { + if (event.keyCode === DEL) { + this._dissectNumber.value = this._dissectNumber.value.substring( + 0, + this._dissectNumber.value.length - 1 + ); + } + }; + + this._dissectNumber.oninput = (event) => { + // Put a limit on the size (2 <--> 128). + this._dissectNumber.onmouseout = function () { + this._dissectNumber.value = Math.max( + this._dissectNumber.value, + 2 + ); + }; + + this._dissectNumber.value = Math.max( + Math.min(this._dissectNumber.value, 128), + 2 + ); + }; + + widgetWindow.addButton( + "restore-button.svg", + iconSize, + _("Undo") + ).onclick = function () { + this._undo(); + }; + + //.TRANS: user can tap out a rhythm by clicking on a ruler. + this._tapButton = widgetWindow.addButton( + "tap-button.svg", + iconSize, + _("Tap a rhythm") + ); + this._tapButton.onclick = () => { + this._tap(); + }; + + //.TRANS: clear all subdivisions from the ruler. + widgetWindow.addButton( + "erase-button.svg", + iconSize, + _("Clear") + ).onclick = function () { + this._clear(); + }; + + // We use an outer div to scroll vertically and an inner div to + // scroll horizontally. + let rhythmRulerTable = document.createElement("table"); + widgetWindow.getWidgetBody().append(rhythmRulerTable); + + let wMax = 0; + // Each row in the ruler table contains a play button in the + // first column and a ruler table in the second column. + for (let i = 0; i < this.Rulers.length; i++) { + let rhythmRulerTableRow = rhythmRulerTable.insertRow(); + + if (beginnerMode) { + let w = 0; + for (let r = 0; r < this.Rulers[i][0].length; r++) { + w += 580 / this.Rulers[i][0][r]; + } + + if (w > wMax) { + rhythmRulerTable.style.width = w + "px"; + wMax = w; + } + } else { + let drumcell = rhythmRulerTableRow.insertCell(); + drumcell.innerHTML = + '' +
+                    _('; + drumcell.className = "headcol"; // Position fixed when scrolling horizontally + + drumcell.onclick = (function (id) { + return function () { + if (this._playing) { + if (this._rulerPlaying === id) { + this.innerHTML = + '' +
+                                    _('; + this._playing = false; + this._playingOne = false; + this._playingAll = false; + this._rulerPlaying = -1; + this._startingTime = null; + this._elapsedTimes[id] = 0; + this._offsets[id] = 0; + setTimeout( + this._calculateZebraStripes(id), + 1000 + ); + } + } else { + if (this._playingOne === false) { + this._rulerSelected = id; + logo.turtleDelay = 0; + this._playing = true; + this._playingOne = true; + this._playingAll = false; + this._cellCounter = 0; + this._startingTime = null; + this._rulerPlaying = id; + this.innerHTML = + '' +
+                                    _('; + this._elapsedTimes[id] = 0; + this._offsets[id] = 0; + this._playOne(); + } + } + }; + })(i); + } + + let rulerCell = rhythmRulerTableRow.insertCell(); + // Create individual rulers as tables. + rulerCell.innerHTML = + '
'; + + let rulerCellTable = docById("rulerCellTable" + i); + rulerCellTable.style.textAlign = "center"; + rulerCellTable.style.border = "0px"; + rulerCellTable.style.borderCollapse = "collapse"; + rulerCellTable.cellSpacing = "0px"; + rulerCellTable.cellPadding = "0px"; + let rulerRow = rulerCellTable.insertRow(); + this._rulers[i] = rulerRow; + rulerRow.setAttribute("data-row", i); + + for (let j = 0; j < this.Rulers[i][0].length; j++) { + let noteValue = this.Rulers[i][0][j]; + let rulerSubCell = rulerRow.insertCell(-1); + rulerSubCell.innerHTML = calcNoteValueToDisplay( + noteValue, + 1, + this._cellScale + ); + rulerSubCell.style.height = RULERHEIGHT + "px"; + rulerSubCell.style.minHeight = rulerSubCell.style.height; + rulerSubCell.style.maxHeight = rulerSubCell.style.height; + rulerSubCell.style.width = this._noteWidth(noteValue) + "px"; + rulerSubCell.style.minWidth = rulerSubCell.style.width; + rulerSubCell.style.border = "0px"; + rulerSubCell.border = "0px"; + rulerSubCell.padding = "0px"; + rulerSubCell.style.padding = "0px"; + rulerSubCell.style.lineHeight = 60 + " % "; + if (i % 2 === 0) { + if (j % 2 === 0) { + rulerSubCell.style.backgroundColor = + platformColor.selectorBackground; + } else { + rulerSubCell.style.backgroundColor = + platformColor.selectorSelected; + } + } else { + if (j % 2 === 0) { + rulerSubCell.style.backgroundColor = + platformColor.selectorSelected; + } else { + rulerSubCell.style.backgroundColor = + platformColor.selectorBackground; + } + } + + this.__addCellEventHandlers( + rulerSubCell, + this._noteWidth(noteValue), + noteValue + ); + } + + // Match the play button height to the ruler height. + rhythmRulerTableRow.cells[0].style.width = BUTTONSIZE + "px"; + rhythmRulerTableRow.cells[0].style.minWidth = BUTTONSIZE + "px"; + rhythmRulerTableRow.cells[0].style.maxWidth = BUTTONSIZE + "px"; + rhythmRulerTableRow.cells[0].style.height = + rulerRow.offsetHeight + "px"; + rhythmRulerTableRow.cells[0].style.minHeight = + rulerRow.offsetHeight + "px"; + rhythmRulerTableRow.cells[0].style.maxHeight = + rulerRow.offsetHeight + "px"; + rhythmRulerTableRow.cells[0].style.verticalAlign = "middle"; + } + + // Restore dissect history. + let cell; + for (let drum = 0; drum < this.Drums.length; drum++) { + if (this.Drums[i] === null) { + continue; } - const newStack = [ - [ - 0, - [ - "action", - { - collapsed: true - } - ], - 100 + delta, - 100 + delta, - [null, 1, 2, null] - ], - [ - 1, - [ - "text", - { - value: action_name - } - ], - 0, - 0, - [0] - ] - ]; - newStack.push([2, "settimbre", 0, 0, [0, 3, 5, 4]]); - newStack.push([ - 3, - [ - "voicename", - { - value: voice - } - ], - 0, - 0, - [2] - ]); - newStack.push([4, "hidden", 0, 0, [2, null]]); - let previousBlock = 2; - let sameNoteValue = 1; - for (let i = 0; i < ruler.cells.length; i++) { - if (noteValues[i] === noteValues[i + 1] && i < ruler.cells.length - 1) { - sameNoteValue += 1; + for (let i = 0; i < this._dissectHistory.length; i++) { + if (this._dissectHistory[i][1] !== this.Drums[drum]) { continue; - } else { - const idx = newStack.length; - const noteValue = noteValues[i]; + } + + let rhythmRulerTableRow = this._rulers[drum]; + for (let j = 0; j < this._dissectHistory[i][0].length; j++) { + if (this._dissectHistory[i][0][j] == undefined) { + continue; + } - const obj = rationalToFraction(1 / Math.abs(noteValue)); + this._rulerSelected = drum; - if (sameNoteValue === 1) { - // Add a note block. - if (noteValue < 0) { - newStack.push([ - idx, - "newnote", - 0, - 0, - [previousBlock, idx + 1, idx + 4, idx + 7] - ]); - newStack.push([idx + 1, "divide", 0, 0, [idx, idx + 2, idx + 3]]); - newStack.push([ - idx + 2, - [ - "number", - { - value: obj[0] - } - ], - 0, - 0, - [idx + 1] - ]); - newStack.push([ - idx + 3, - [ - "number", - { - value: obj[1] - } - ], - 0, - 0, - [idx + 1] - ]); - newStack.push([idx + 4, "vspace", 0, 0, [idx, idx + 5]]); - newStack.push([idx + 5, "rest2", 0, 0, [idx + 4, idx + 6]]); - newStack.push([idx + 6, "hidden", 0, 0, [idx + 5, null]]); - if (i == ruler.cells.length - 1) { - newStack.push([idx + 7, "hidden", 0, 0, [idx, null]]); + if (typeof this._dissectHistory[i][0][j] === "number") { + cell = + rhythmRulerTableRow.cells[ + this._dissectHistory[i][0][j] + ]; + this.__toggleRestState(cell, false); + } else if ( + typeof this._dissectHistory[i][0][j][0] === "number" + ) { + if ( + typeof this._dissectHistory[i][0][j][1] === "number" + ) { + // dissect is [cell, num] + cell = + rhythmRulerTableRow.cells[ + this._dissectHistory[i][0][j][0] + ]; + if (cell != undefined) { + this.__dissectByNumber( + cell, + this._dissectHistory[i][0][j][1], + false + ); } else { - newStack.push([idx + 7, "hidden", 0, 0, [idx, idx + 8]]); - previousBlock = idx + 7; + console.warn( + "Could not find cell to divide. Did the order of the rhythm blocks change?" + ); } } else { - newStack.push([ - idx, - "newnote", - 0, - 0, - [previousBlock, idx + 1, idx + 4, idx + 8] - ]); - newStack.push([idx + 1, "divide", 0, 0, [idx, idx + 2, idx + 3]]); - newStack.push([ - idx + 2, - [ - "number", - { - value: obj[0] - } - ], - 0, - 0, - [idx + 1] - ]); - newStack.push([ - idx + 3, - [ - "number", - { - value: obj[1] - } - ], - 0, - 0, - [idx + 1] - ]); - newStack.push([idx + 4, "vspace", 0, 0, [idx, idx + 5]]); - newStack.push([ - idx + 5, - "pitch", - 0, - 0, - [idx + 4, idx + 6, idx + 7, null] - ]); - newStack.push([ - idx + 6, - [ - "notename", - { - value: "C" - } - ], - 0, - 0, - [idx + 5] - ]); - newStack.push([ - idx + 7, - [ - "number", - { - value: 4 - } - ], - 0, - 0, - [idx + 5] - ]); - if (i == ruler.cells.length - 1) { - newStack.push([idx + 8, "hidden", 0, 0, [idx, null]]); - } else { - newStack.push([idx + 8, "hidden", 0, 0, [idx, idx + 9]]); - previousBlock = idx + 8; + // divide is [cell, [values]] + cell = + rhythmRulerTableRow.cells[ + this._dissectHistory[i][0][j][0] + ]; + if (cell != undefined) { + this.__divideFromList( + cell, + this._dissectHistory[i][0][j][1], + false + ); } } } else { - // Add a note block inside a repeat block. - if (i == ruler.cells.length - 1) { - newStack.push([ - idx, - "repeat", - 0, - 0, - [previousBlock, idx + 1, idx + 2, null] - ]); - } else { - newStack.push([ - idx, - "repeat", - 0, - 0, - [previousBlock, idx + 1, idx + 2, idx + 11] - ]); - previousBlock = idx; - } - newStack.push([ - idx + 1, - [ - "number", - { - value: sameNoteValue - } - ], - 0, - 0, - [idx] - ]); - if (noteValue < 0) { - newStack.push([ - idx + 2, - "newnote", - 0, - 0, - [idx, idx + 3, idx + 6, idx + 9] - ]); - newStack.push([idx + 3, "divide", 0, 0, [idx + 2, idx + 4, idx + 5]]); - newStack.push([ - idx + 4, - [ - "number", - { - value: 1 - } - ], - 0, - 0, - [idx + 3] - ]); - newStack.push([ - idx + 5, - [ - "number", - { - value: -noteValue - } - ], - 0, - 0, - [idx + 3] - ]); - newStack.push([idx + 6, "vspace", 0, 0, [idx + 2, idx + 7]]); - newStack.push([idx + 7, "rest2", 0, 0, [idx + 6, idx + 8]]); - newStack.push([idx + 8, "hidden", 0, 0, [idx + 7, null]]); - newStack.push([idx + 9, "hidden", 0, 0, [idx + 2, null]]); - } else { - newStack.push([ - idx + 2, - "newnote", - 0, - 0, - [idx, idx + 3, idx + 6, idx + 10] - ]); - newStack.push([idx + 3, "divide", 0, 0, [idx + 2, idx + 4, idx + 5]]); - newStack.push([ - idx + 4, - [ - "number", - { - value: 1 - } - ], - 0, - 0, - [idx + 3] - ]); - newStack.push([ - idx + 5, - [ - "number", - { - value: noteValue - } - ], - 0, - 0, - [idx + 3] - ]); - newStack.push([idx + 6, "vspace", 0, 0, [idx + 2, idx + 7]]); - newStack.push([ - idx + 7, - "pitch", - 0, - 0, - [idx + 6, idx + 8, idx + 9, null] - ]); - newStack.push([ - idx + 8, - [ - "notename", - { - value: "C" - } - ], - 0, - 0, - [idx + 7] - ]); - newStack.push([ - idx + 9, - [ - "number", - { - value: 4 - } - ], - 0, - 0, - [idx + 7] - ]); - newStack.push([idx + 10, "hidden", 0, 0, [idx + 2, null]]); + // tie is [[cell, value], [cell, value]...] + let history = this._dissectHistory[i][0][j]; + this._mouseDownCell = + rhythmRulerTableRow.cells[history[0][0]]; + this._mouseUpCell = + rhythmRulerTableRow.cells[last(history)[0]]; + if (this._mouseUpCell != undefined) { + this.__tie(false); } - } - - sameNoteValue = 1; - } - } - - logo.blocks.loadNewBlocks(newStack); - if (selectedRuler > this.Rulers.length - 2) { - return; - } else { - this._saveMachine(selectedRuler + 1); - } - }, 500); - } - _mergeRulers() { - // Merge the rulers into one set of rhythms. - const rList = []; - let noteValues; - for (let r = 0; r < this.Rulers.length; r++) { - let t = 0; - const selectedRuler = this.Rulers[r]; - noteValues = selectedRuler[0]; - for (let i = 0; i < noteValues.length; i++) { - t += 1 / noteValues[i]; - if (rList.indexOf(t) === -1) { - rList.push(t); + this._mouseDownCell = null; + this._mouseUpCell = null; + } } } } - rList.sort(function (a, b) { - return a - b; - }); - - noteValues = []; - for (let i = 0; i < rList.length; i++) { - if (i === 0) { - noteValues.push(1 / rList[i]); - } else { - noteValues.push(1 / (rList[i] - rList[i - 1])); - } - } - - return noteValues; - } - - _get_save_lock() { - return this._save_lock; - } + logo.textMsg(_("Click on the ruler to divide it.")); + // this._piemenuRuler(this._rulerSelected); + }; - saveDissectHistory() { + saveDissectHistory () { // Save the new dissect history. - const dissectHistory = []; - const drums = []; + + let dissectHistory = []; + let drums = []; let drum; let history; for (let i = 0; i < this.Rulers.length; i++) { @@ -2598,18 +2768,17 @@ class RhythmRuler { for (let i = 0; i < this._dissectHistory.length; i++) { drum = this._dissectHistory[i][1]; if (drums.indexOf(drum) === -1) { - history = JSON.parse(JSON.stringify(this._dissectHistory[i][0])); + history = JSON.parse( + JSON.stringify(this._dissectHistory[i][0]) + ); dissectHistory.push([history, drum]); } } this._dissectHistory = JSON.parse(JSON.stringify(dissectHistory)); - } + }; - /** - * @deprecated - */ - _piemenuRuler(/* selectedRuler */) { + _piemenuRuler (selectedRuler) { return; // In progress /* // piemenu version of ruler @@ -2656,9 +2825,9 @@ class RhythmRuler { this._wheel.createWheel(); */ - } + }; - _piemenuNumber(wheelValues, selectedValue) { + _piemenuNumber (wheelValues, selectedValue) { // input form and wheelNav pie menu for number selection docById("wheelDiv").style.display = ""; @@ -2667,7 +2836,7 @@ class RhythmRuler { // exit button this._exitWheel = new wheelnav("_exitWheel", this._numberWheel.raphael); - const wheelLabels = []; + let wheelLabels = []; for (let i = 0; i < wheelValues.length; i++) { wheelLabels.push(wheelValues[i].toString()); } @@ -2705,12 +2874,15 @@ class RhythmRuler { this._exitWheel.clickModeRotate = false; this._exitWheel.createWheel(["x", " "]); - const __selectionChanged = () => { - this._dissectNumber.value = wheelValues[this._numberWheel.selectedNavItemIndex]; + + + let __selectionChanged = function () { + this._dissectNumber.value = + wheelValues[this._numberWheel.selectedNavItemIndex]; }; - const __exitMenu = () => { - const d = new Date(); + let __exitMenu = function () { + let d = new Date(); this._piemenuExitTime = d.getTime(); docById("wheelDiv").style.display = "none"; this._numberWheel.removeWheel(); @@ -2729,19 +2901,19 @@ class RhythmRuler { // Hide the widget when the selection is made. for (let i = 0; i < wheelLabels.length; i++) { - this._numberWheel.navItems[i].navigateFunction = function () { + this._numberWheel.navItems[i].navigateFunction = () => { __selectionChanged(); __exitMenu(); }; } // Or use the exit wheel... - this._exitWheel.navItems[0].navigateFunction = function () { + this._exitWheel.navItems[0].navigateFunction = () => { __exitMenu(); }; - } + }; - _positionWheel() { + _positionWheel () { if (docById("wheelDiv").style.display == "none") { return; } @@ -2751,9 +2923,9 @@ class RhythmRuler { docById("wheelDiv").style.width = "300px"; // Position the widget over the note block. - const x = this._left + 100; - const y = this._top; - const selectorWidth = 150; + let x = this._left + 100; + let y = this._top; + let selectorWidth = 150; docById("wheelDiv").style.left = Math.min( @@ -2765,5 +2937,5 @@ class RhythmRuler { } else { docById("wheelDiv").style.top = y - 300 + "px"; } - } -} + }; +} \ No newline at end of file From 5fe54ccc4c5a0f2fe25481bc50d07526fa91f9d4 Mon Sep 17 00:00:00 2001 From: ricknjacky Date: Sat, 30 Jan 2021 18:25:23 +0530 Subject: [PATCH 3/7] resolving the regressions --- js/widgets/rhythmruler.js | 208 ++++++++++++++++++-------------------- 1 file changed, 101 insertions(+), 107 deletions(-) diff --git a/js/widgets/rhythmruler.js b/js/widgets/rhythmruler.js index a1e5f0fc60..789738d700 100644 --- a/js/widgets/rhythmruler.js +++ b/js/widgets/rhythmruler.js @@ -17,7 +17,6 @@ // rulerTableDiv is for the drum buttons (fixed first col) and the ruler cells class RhythmRuler { - static ROWHEIGHT = 130; static RULERHEIGHT = 70; static BUTTONSIZE = 51; static ICONSIZE = 32; @@ -80,7 +79,7 @@ class RhythmRuler { } for (let i = 0; i < ruler.cells.length; i++) { - let newCell = ruler.cells[i]; + const newCell = ruler.cells[i]; newCell.style.border = "2px solid lightgrey"; newCell.style.borderRadius = "10px"; if (evenColor === platformColor.selectorBackground) { @@ -106,7 +105,7 @@ class RhythmRuler { }; _dissectRuler (event, ruler) { - let cell = event.target; + const cell = event.target; if (cell === null) { return; } @@ -117,7 +116,7 @@ class RhythmRuler { return; } - let cellParent = cell.parentNode; + const cellParent = cell.parentNode; if (cellParent === null) { return; } @@ -141,16 +140,15 @@ class RhythmRuler { this._tapMode = false; this._tapTimes = []; this._tapEndTime = null; - let iconSize = ICONSIZE; this._tapButton.innerHTML = '' +
                         _('; return; } @@ -158,9 +156,7 @@ class RhythmRuler { this._tapTimes = []; // Play a count off before starting tapping. - let interval = - this._bpmFactor / - Math.abs(noteValues[this._tapCell.cellIndex]); + const interval = this._bpmFactor / Math.abs(noteValues[this._tapCell.cellIndex]); let drum; if (this.Drums[this._rulerSelected] === null) { @@ -175,14 +171,14 @@ class RhythmRuler { // FIXME: Should be based on meter for (let i = 0; i < 4; i++) { - setTimeout(function () { + setTimeout(() => { logo.synth.trigger( 0, "C4", Singer.defaultBPMFactor / 16, drum, null, null ); }, (interval * i) / 4); } - setTimeout(function () { + setTimeout(() => { this.__startTapping(noteValues, interval); }, interval); } @@ -207,30 +203,30 @@ class RhythmRuler { this.saveDissectHistory(); }; - __startTapping (noteValues, interval) { - let d = new Date(); + __startTapping (noteValues, interval, event) { + const d = new Date(); this._tapTimes = [d.getTime()]; this._tapEndTime = this._tapTimes[0] + interval; // Set a timeout to end tapping - setTimeout(function () { - this.__endTapping(); + setTimeout(() => { + this.__endTapping(event); }, interval); // Display a progress bar. - function __move(tick, stepSize) { + const __move = (tick, stepSize) => { let width = 1; - let id = setInterval(frame, tick); - function frame() { + + const id = setInterval(() => { if (width >= 100) { clearInterval(id); } else { width += stepSize; this._progressBar.style.width = width + "%"; } - } + }, tick); } this._tapCell.innerHTML = "
"; @@ -239,7 +235,8 @@ class RhythmRuler { __move(interval / 8, 100 / 8); }; - __endTapping () { + __endTapping (event) { + const cell = event.target; if (cell.parentNode === null) { console.debug("Null parent node in endTapping"); return; @@ -249,7 +246,7 @@ class RhythmRuler { if (this._progressBar) this._progressBar.remove(); this._tapCell.innerHTML = ""; - let d = new Date(); + const d = new Date(); this._tapTimes.push(d.getTime()); this._tapMode = false; @@ -300,13 +297,13 @@ class RhythmRuler { break; } - let newNoteValues = []; + const newNoteValues = []; let sum = 0; let obj; - let interval = - this._bpmFactor / Math.abs(noteValues[this._tapCell.cellIndex]); + // let interval = + // this._bpmFactor / Math.abs(noteValues[this._tapCell.cellIndex]); for (let i = 1; i < this._tapTimes.length; i++) { - let dtime = this._tapTimes[i] - this._tapTimes[i - 1]; + const dtime = this._tapTimes[i] - this._tapTimes[i - 1]; if (i < this._tapTimes.length - 1) { obj = nearestBeat( (100 * dtime) / this._bpmFactor, @@ -338,24 +335,24 @@ class RhythmRuler { this._tapTimes = []; this._tapCell = null; this._tapEndTime = null; - let iconSize = ICONSIZE; + // let iconSize = RhythmRuler.ICONSIZE; this._tapButton.innerHTML = '' +
             _('; }; __addCellEventHandlers (cell, cellWidth, noteValue) { - __mouseOverHandler = function (event) { - let cell = event.target; + const __mouseOverHandler = (event) => { + const cell = event.target; if (cell === null || cell.parentNode === null) { return; } @@ -382,20 +379,20 @@ class RhythmRuler { } }; - __mouseOutHandler = function (event) { - let cell = event.target; + const __mouseOutHandler = (event) => { + const cell = event.target; cell.innerHTML = ""; }; - __mouseDownHandler = function (event) { - let cell = event.target; + const __mouseDownHandler = (event) => { + const cell = event.target; this._mouseDownCell = cell; - let d = new Date(); + const d = new Date(); this._longPressStartTime = d.getTime(); this._inLongPress = false; - this._longPressBeep = setTimeout(function () { + this._longPressBeep = setTimeout(() => { // Removing audio feedback on long press since it // occasionally confuses tone.js during rapid clicking // in the widget. @@ -415,15 +412,15 @@ class RhythmRuler { }, 1500); }; - __mouseUpHandler = function (event) { + const __mouseUpHandler = (event) => { clearTimeout(this._longPressBeep); - let cell = event.target; + const cell = event.target; this._mouseUpCell = cell; if (this._mouseDownCell !== this._mouseUpCell) { this._tieRuler(event, cell.parentNode.getAttribute("data-row")); } else if (this._longPressStartTime !== null && !this._tapMode) { - let d = new Date(); - let elapseTime = d.getTime() - this._longPressStartTime; + const d = new Date(); + const elapseTime = d.getTime() - this._longPressStartTime; if (elapseTime > 1500) { this._inLongPress = true; this.__toggleRestState(this, true); @@ -435,7 +432,7 @@ class RhythmRuler { this._longPressStartTime = null; }; - __clickHandler = function (event) { + const __clickHandler = (event) => { if (event == undefined) return; if (!this.__getLongPressStatus()) { let cell = event.target; @@ -492,7 +489,7 @@ class RhythmRuler { let noteValues = this.Rulers[this._rulerSelected][0]; let noteValue = noteValues[cell.cellIndex]; - __mouseOverHandler = function (event) { + const __mouseOverHandler = (event) => { let cell = event.target; if (cell === null) { return; @@ -527,7 +524,7 @@ class RhythmRuler { } }; - __mouseOutHandler = function (event) { + const __mouseOutHandler = (event) => { let cell = event.target; cell.innerHTML = ""; }; @@ -577,7 +574,7 @@ class RhythmRuler { } let ruler = this._rulers[this._rulerSelected]; - let newCellIndex = cell.cellIndex; + const newCellIndex = cell.cellIndex; if ( typeof this._rulerSelected === "string" || @@ -594,19 +591,19 @@ class RhythmRuler { ruler.deleteCell(newCellIndex); - let noteValue = noteValues[newCellIndex]; - let tempwidth = this._noteWidth(newNoteValue); + // let noteValue = noteValues[newCellIndex]; + // let tempwidth = this._noteWidth(newNoteValue); noteValues.splice(newCellIndex, 1); for (let i = 0; i < newNoteValues.length; i++) { - let newCell = ruler.insertCell(newCellIndex + i); - let newNoteValue = newNoteValues[i]; - let newCellWidth = parseFloat(this._noteWidth(newNoteValue)); + const newCell = ruler.insertCell(newCellIndex + i); + const newNoteValue = newNoteValues[i]; + const newCellWidth = parseFloat(this._noteWidth(newNoteValue)); noteValues.splice(newCellIndex + i, 0, newNoteValue); newCell.style.width = newCellWidth + "px"; newCell.style.minWidth = newCell.style.width; - newCell.style.height = RULERHEIGHT + "px"; + newCell.style.height = RhythmRuler.RULERHEIGHT + "px"; newCell.style.minHeight = newCell.style.height; newCell.style.maxHeight = newCell.style.height; @@ -633,7 +630,7 @@ class RhythmRuler { } let ruler = this._rulers[this._rulerSelected]; - let newCellIndex = cell.cellIndex; + const newCellIndex = cell.cellIndex; if ( typeof this._rulerSelected === "string" || @@ -664,13 +661,11 @@ class RhythmRuler { newNoteValue = inputNum * noteValue; - let tempwidth = this._noteWidth(newNoteValue); - let tempwidthPixels = - parseFloat(inputNum) * parseFloat(tempwidth) + "px"; - let difference = + const tempwidth = this._noteWidth(newNoteValue); + const difference = parseFloat(this._noteWidth(noteValue)) - parseFloat(inputNum) * parseFloat(tempwidth); - let newCellWidth = + const newCellWidth = parseFloat(this._noteWidth(newNoteValue)) + parseFloat(difference) / inputNum; noteValues.splice(newCellIndex, 1); @@ -681,7 +676,7 @@ class RhythmRuler { newCell.style.width = newCellWidth + "px"; newCell.style.minWidth = newCell.style.width; - newCell.style.height = RULERHEIGHT + "px"; + newCell.style.height = RhythmRuler.RULERHEIGHT + "px"; newCell.style.minHeight = newCell.style.height; newCell.style.maxHeight = newCell.style.height; @@ -776,7 +771,7 @@ class RhythmRuler { this.Rulers[this._rulerSelected][0].splice(i, 1); } - let newCellWidth = this._noteWidth(1 / noteValue); + const newCellWidth = this._noteWidth(1 / noteValue); // Use noteValue of downCell for REST status. if (oldNoteValue < 0) { noteValues[downCellIndex] = -1 / noteValue; @@ -786,7 +781,7 @@ class RhythmRuler { this._mouseDownCell.style.width = newCellWidth + "px"; this._mouseDownCell.style.minWidth = this._mouseDownCell.style.width; - this._mouseDownCell.style.height = RULERHEIGHT + "px"; + this._mouseDownCell.style.height = RhythmRuler.RULERHEIGHT + "px"; this._mouseDownCell.style.minHeight = this._mouseDownCell.style.height; this._mouseDownCell.style.maxHeight = this._mouseDownCell.style.height; @@ -832,10 +827,10 @@ class RhythmRuler { let oldCellNoteValue = noteValues[newCellIndex]; let newNoteValue = oldCellNoteValue / inputNum; - let newCell = ruler.insertCell(newCellIndex); + const newCell = ruler.insertCell(newCellIndex); newCell.style.width = this._noteWidth(newNoteValue) + "px"; newCell.style.minWidth = newCell.style.width; - newCell.style.height = RULERHEIGHT + "px"; + newCell.style.height = RhythmRuler.RULERHEIGHT + "px"; newCell.style.minHeight = newCell.style.height; newCell.style.maxHeight = newCell.style.height; @@ -869,10 +864,10 @@ class RhythmRuler { let newNoteValue = 1 / sum; let newCellWidth = this._noteWidth(newNoteValue); - let newCell = ruler.insertCell(newCellIndex); + const newCell = ruler.insertCell(newCellIndex); newCell.style.width = newCellWidth + "px"; newCell.style.minWidth = newCell.style.width; - newCell.style.height = RULERHEIGHT + "px"; + newCell.style.height = RhythmRuler.RULERHEIGHT + "px"; newCell.style.minHeight = newCell.style.height; newCell.style.maxHeight = newCell.style.height; @@ -903,7 +898,7 @@ class RhythmRuler { let oldCellWidth = this._noteWidth(history[0][1]); oldCell.style.width = oldCellWidth + "px"; oldCell.style.minWidth = oldCell.style.width; - oldCell.style.height = RULERHEIGHT + "px"; + oldCell.style.height = RhythmRuler.RULERHEIGHT + "px"; oldCell.style.minHeight = oldCell.style.height; oldCell.style.maxHeight = oldCell.style.height; @@ -915,11 +910,11 @@ class RhythmRuler { ); for (let i = 1; i < history.length; i++) { - let newCell = ruler.insertCell(history[0][0] + i); + const newCell = ruler.insertCell(history[0][0] + i); let newCellWidth = this._noteWidth(history[i][1]); newCell.style.width = newCellWidth + "px"; newCell.style.minWidth = newCell.style.width; - newCell.style.height = RULERHEIGHT + "px"; + newCell.style.height = RhythmRuler.RULERHEIGHT + "px"; newCell.style.minHeight = newCell.style.height; newCell.style.maxHeight = newCell.style.height; @@ -943,7 +938,7 @@ class RhythmRuler { } } else if (obj[0] === "rest") { let newCellIndex = last(divisionHistory); - let cell = ruler.cells[newCellIndex]; + const cell = ruler.cells[newCellIndex]; this.__toggleRestState(cell, false); divisionHistory.pop(); } @@ -956,7 +951,7 @@ class RhythmRuler { _tap () { this._tapMode = true; - let iconSize = ICONSIZE; + let iconSize = RhythmRuler.ICONSIZE; this._tapButton.innerHTML = '' +
             _('; for (r = 0; r < this.Rulers.length; r++) { this._rulerSelected = r; @@ -999,16 +993,16 @@ class RhythmRuler { }; __pause () { - let iconSize = ICONSIZE; + this._playAllCell.innerHTML = '' +
             _('; this._playing = false; this._playingAll = false; @@ -1028,7 +1022,7 @@ class RhythmRuler { // Wait for pause to complete before restarting. this._playingAll = true; - setTimeout(function () { + setTimeout(() => { this.__resume(); }, 1000); } @@ -1038,16 +1032,16 @@ class RhythmRuler { }; __resume () { - let iconSize = ICONSIZE; + this._playAllCell.innerHTML = '' +
             _('; logo.turtleDelay = 0; this._playingAll = true; @@ -1084,7 +1078,7 @@ class RhythmRuler { logo.synth.stop(); logo.resetSynth(0); if (this._startingTime === null) { - let d = new Date(); + const d = new Date(); this._startingTime = d.getTime(); this._elapsedTimes[this._rulerSelected] = 0; this._offsets[this._rulerSelected] = 0; @@ -1106,7 +1100,7 @@ class RhythmRuler { this._calculateZebraStripes(rulerNo); } - let cell = ruler.cells[colIndex]; + const cell = ruler.cells[colIndex]; let noteValues = this.Rulers[rulerNo][0]; let noteValue = noteValues[colIndex]; @@ -1172,7 +1166,7 @@ class RhythmRuler { d.getTime() - this._startingTime - this._elapsedTimes[rulerNo]; } - setTimeout(function () { + setTimeout(() => { colIndex += 1; if (colIndex === noteValues.length) { colIndex = 0; @@ -1195,7 +1189,7 @@ class RhythmRuler { logo.refreshCanvas(); - setTimeout(function () { + setTimeout(() => { let ruler = this._rulers[selectedRuler]; let noteValues = this.Rulers[selectedRuler][0]; // Get the first word of drum's name (ignore the word 'drum' itself) @@ -1314,7 +1308,7 @@ class RhythmRuler { logo.refreshCanvas(); - setTimeout(function () { + setTimeout(() => { let ruler = this._rulers[selectedRuler]; let noteValues = this.Rulers[selectedRuler][0]; let stack_value; @@ -1605,7 +1599,7 @@ class RhythmRuler { logo.refreshCanvas(); - setTimeout(function () { + setTimeout(() => { let ruler = this._rulers[selectedRuler]; let noteValues = this.Rulers[selectedRuler][0]; let delta = selectedRuler * 42; @@ -1903,13 +1897,13 @@ class RhythmRuler { _saveVoiceMachine (selectedRuler, voice) { - for (let name in logo.blocks.palettes.dict) { + for (const name in logo.blocks.palettes.dict) { logo.blocks.palettes.dict[name].hideMenu(true); } logo.refreshCanvas(); - setTimeout(function () { + setTimeout(() => { let ruler = this._rulers[selectedRuler]; let noteValues = this.Rulers[selectedRuler][0]; let delta = selectedRuler * 42; @@ -1925,9 +1919,9 @@ class RhythmRuler { // This should never happen. let action_name; if (this.Drums[selectedRuler] === null) { - let action_name = _("guitar") + " " + _("action"); + action_name = _("guitar") + " " + _("action"); } else { - let action_name = + action_name = logo.blocks.blockList[ logo.blocks.blockList[this.Drums[selectedRuler]] .connections[1] @@ -2276,7 +2270,7 @@ class RhythmRuler { _mergeRulers () { // Merge the rulers into one set of rhythms. - rList = []; + const rList = []; let noteValues; for (let r = 0; r < this.Rulers.length; r++) { let t = 0; @@ -2290,7 +2284,7 @@ class RhythmRuler { } } - rList.sort(function (a, b) { + rList.sort((a, b) => { return a - b; }); @@ -2337,7 +2331,7 @@ class RhythmRuler { let w = window.innerWidth; this._cellScale = 1.0; - let iconSize = ICONSIZE; + let iconSize = RhythmRuler.ICONSIZE; let widgetWindow = window.widgetWindows.windowFor(this, "rhythm maker"); this.widgetWindow = widgetWindow; @@ -2347,7 +2341,7 @@ class RhythmRuler { // For the button callbacks - widgetWindow.onclose = function () { + widgetWindow.onclose = () => { // If the piemenu was open, close it. // docById('wheelDiv').style.display = 'none'; // docById('contextWheelDiv').style.display = 'none'; @@ -2409,7 +2403,7 @@ class RhythmRuler { "export-chunk.svg", iconSize, _("Save rhythms") - ).onclick = async function () { + ).onclick = async () => { // this._save(0); // Debounce button if (!this._get_save_lock()) { @@ -2429,7 +2423,7 @@ class RhythmRuler { "export-drums.svg", iconSize, _("Save drum machine") - ).onclick = async function () { + ).onclick = async () => { // Debounce button if (!this._get_save_lock()) { this._save_lock = true; @@ -2447,7 +2441,7 @@ class RhythmRuler { }; this._dissectNumber.onkeydown = (event) => { - if (event.keyCode === DEL) { + if (event.keyCode === RhythmRuler.DEL) { this._dissectNumber.value = this._dissectNumber.value.substring( 0, this._dissectNumber.value.length - 1 @@ -2457,7 +2451,7 @@ class RhythmRuler { this._dissectNumber.oninput = (event) => { // Put a limit on the size (2 <--> 128). - this._dissectNumber.onmouseout = function () { + this._dissectNumber.onmouseout = () => { this._dissectNumber.value = Math.max( this._dissectNumber.value, 2 @@ -2474,7 +2468,7 @@ class RhythmRuler { "restore-button.svg", iconSize, _("Undo") - ).onclick = function () { + ).onclick = () => { this._undo(); }; @@ -2493,7 +2487,7 @@ class RhythmRuler { "erase-button.svg", iconSize, _("Clear") - ).onclick = function () { + ).onclick = () => { this._clear(); }; @@ -2532,8 +2526,8 @@ class RhythmRuler { '" />'; drumcell.className = "headcol"; // Position fixed when scrolling horizontally - drumcell.onclick = (function (id) { - return function () { + drumcell.onclick = ((id) => { + return () => { if (this._playing) { if (this._rulerPlaying === id) { this.innerHTML = @@ -2610,7 +2604,7 @@ class RhythmRuler { 1, this._cellScale ); - rulerSubCell.style.height = RULERHEIGHT + "px"; + rulerSubCell.style.height = RhythmRuler.RULERHEIGHT + "px"; rulerSubCell.style.minHeight = rulerSubCell.style.height; rulerSubCell.style.maxHeight = rulerSubCell.style.height; rulerSubCell.style.width = this._noteWidth(noteValue) + "px"; @@ -2646,9 +2640,9 @@ class RhythmRuler { } // Match the play button height to the ruler height. - rhythmRulerTableRow.cells[0].style.width = BUTTONSIZE + "px"; - rhythmRulerTableRow.cells[0].style.minWidth = BUTTONSIZE + "px"; - rhythmRulerTableRow.cells[0].style.maxWidth = BUTTONSIZE + "px"; + rhythmRulerTableRow.cells[0].style.width = RhythmRuler.BUTTONSIZE + "px"; + rhythmRulerTableRow.cells[0].style.minWidth = RhythmRuler.BUTTONSIZE + "px"; + rhythmRulerTableRow.cells[0].style.maxWidth = RhythmRuler.BUTTONSIZE + "px"; rhythmRulerTableRow.cells[0].style.height = rulerRow.offsetHeight + "px"; rhythmRulerTableRow.cells[0].style.minHeight = @@ -2876,12 +2870,12 @@ class RhythmRuler { - let __selectionChanged = function () { + const __selectionChanged = () => { this._dissectNumber.value = wheelValues[this._numberWheel.selectedNavItemIndex]; }; - let __exitMenu = function () { + const __exitMenu = () => { let d = new Date(); this._piemenuExitTime = d.getTime(); docById("wheelDiv").style.display = "none"; From fa9b70257aa2855d42e51c52aa4e19ab649d0fd4 Mon Sep 17 00:00:00 2001 From: ricknjacky Date: Sat, 30 Jan 2021 20:20:54 +0530 Subject: [PATCH 4/7] JSDoc --- js/widgets/rhythmruler.js | 4862 +++++++++++++++++++------------------ 1 file changed, 2538 insertions(+), 2324 deletions(-) diff --git a/js/widgets/rhythmruler.js b/js/widgets/rhythmruler.js index 789738d700..b7d0653eac 100644 --- a/js/widgets/rhythmruler.js +++ b/js/widgets/rhythmruler.js @@ -1,29 +1,46 @@ -// Copyright (c) 2016-19 Walter Bender -// Copyright (c) 2016 Hemant Kasat -// 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 - -// This widget enable us to create a rhythms which can be imported -// into the pitch-time matrix and hence used to create chunks of -// notes. - -// rulerButtonsDiv is for the widget buttons -// rulerTableDiv is for the drum buttons (fixed first col) and the ruler cells - +/** + * @file This contains the prototype of the rhythmruler Widget + * + * @copyright 2016-19 Walter Bender + * @copyright 2016 Hemant Kasat + * + * @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 TONEBPM, Singer, logo, _, delayExecution, docById, calcNoteValueToDisplay, platformColor, + beginnerMode, last, EIGHTHNOTEWIDTH, nearestBeat, rationalToFraction, DRUMNAMES, VOICENAMES, + EFFECTSNAMES, wheelnav, slicePath + */ + +/* exported RhythmRuler */ + +/** + * @abstract + * This widget enable us to create a rhythms which can be imported into the pitch-time matrix and + * hence used to create chunks of notes. + * + * @description + * - `rulerButtonsDiv` is for the widget buttons + * - `rulerTableDiv` is for the drum buttons (fixed first col) and the ruler cells + */ class RhythmRuler { static RULERHEIGHT = 70; static BUTTONSIZE = 51; static ICONSIZE = 32; static DEL = 46; - - constructor(){ + /** + * @constructor + */ + constructor() { // There is one ruler per drum. this.Drums = []; // Rulers, one per drum, contain the subdivisions defined by rhythm blocks. @@ -65,1923 +82,2206 @@ class RhythmRuler { this._rulers = []; } - _noteWidth (noteValue) { - return Math.floor(EIGHTHNOTEWIDTH * (8 / Math.abs(noteValue)) * 4); - }; - - _calculateZebraStripes (rulerno) { - let ruler = this._rulers[rulerno]; - let evenColor; - if (this._rulerSelected % 2 === 0) { - evenColor = platformColor.selectorBackground; - } else { - evenColor = platformColor.selectorSelected; - } + /** + * Initialises the temperament widget. + * @returns {void} + */ + init() { + // console.debug("init RhythmRuler"); - for (let i = 0; i < ruler.cells.length; i++) { - const newCell = ruler.cells[i]; - newCell.style.border = "2px solid lightgrey"; - newCell.style.borderRadius = "10px"; - if (evenColor === platformColor.selectorBackground) { - if (i % 2 === 0) { - newCell.style.backgroundColor = - platformColor.selectorBackground; - } else { - newCell.style.backgroundColor = - platformColor.selectorSelected; - } - } + this._bpmFactor = (1000 * TONEBPM) / Singer.masterBPM; - if (evenColor === platformColor.selectorSelected) { - if (i % 2 === 0) { - newCell.style.backgroundColor = - platformColor.selectorSelected; - } else { - newCell.style.backgroundColor = - platformColor.selectorBackground; - } - } - } - }; + this._playing = false; + this._playingOne = false; + this._playingAll = false; + this._rulerPlaying = -1; + this._startingTime = null; + this._expanded = false; - _dissectRuler (event, ruler) { - const cell = event.target; - if (cell === null) { - return; + // If there are no drums, add one. + if (this.Drums.length === 0) { + this.Drums.push(null); + this.Rulers.push([[1], []]); } - if (this._tapMode && this._tapTimes.length > 0) { - let d = new Date(); - this._tapTimes.push(d.getTime()); - return; + this._elapsedTimes = []; + this._offsets = []; + for (let i = 0; i < this.Rulers.length; i++) { + this._elapsedTimes.push(0); + this._offsets.push(0); } - const cellParent = cell.parentNode; - if (cellParent === null) { - return; - } + let w = window.innerWidth; + this._cellScale = 1.0; + let iconSize = RhythmRuler.ICONSIZE; - this._rulerSelected = ruler; - if (this._rulerSelected == undefined) { - return; - } + let widgetWindow = window.widgetWindows.windowFor(this, "rhythm maker"); + this.widgetWindow = widgetWindow; + widgetWindow.clear(); + widgetWindow.show(); - if (this._playing) { - console.warn("You cannot dissect while widget is playing."); - return; - } else if (this._tapMode) { - // Tap a rhythm by clicking in a cell. - if (this._tapCell === null) { - let noteValues = this.Rulers[this._rulerSelected][0]; - this._tapCell = event.target; - if (noteValues[this._tapCell.cellIndex] < 0) { - // Don't allow tapping in rests. - this._tapCell = null; - this._tapMode = false; - this._tapTimes = []; - this._tapEndTime = null; - this._tapButton.innerHTML = - '' +
-                        _('; - return; - } + // For the button callbacks - this._tapTimes = []; - // Play a count off before starting tapping. - const interval = this._bpmFactor / Math.abs(noteValues[this._tapCell.cellIndex]); + widgetWindow.onclose = () => { + // If the piemenu was open, close it. + // docById('wheelDiv').style.display = 'none'; + // docById('contextWheelDiv').style.display = 'none'; - let drum; - if (this.Drums[this._rulerSelected] === null) { - drum = "snare drum"; - } else { - let drumBlockNo = logo.blocks.blockList[ - this.Drums[this._rulerSelected] - ].connections[1]; - drum = logo.blocks.blockList[drumBlockNo].value; + // Save the new dissect history. + let dissectHistory = []; + let drums = []; + for (let i = 0; i < this.Rulers.length; i++) { + if (this.Drums[i] === null) { + continue; } - - // FIXME: Should be based on meter - for (let i = 0; i < 4; i++) { - setTimeout(() => { - logo.synth.trigger( - 0, "C4", Singer.defaultBPMFactor / 16, drum, null, null - ); - }, (interval * i) / 4); + let history = []; + for (let j = 0; j < this.Rulers[i][1].length; j++) { + history.push(this.Rulers[i][1][j]); } - setTimeout(() => { - this.__startTapping(noteValues, interval); - }, interval); - } - } else { - let noteValues = this.Rulers[this._rulerSelected][0]; - let inputNum = this._dissectNumber.value; - if (inputNum === "" || isNaN(inputNum)) { - inputNum = 2; - } else { - inputNum = Math.abs(Math.floor(inputNum)); + this._dissectNumber.classList.add("hasKeyboard"); + dissectHistory.push([history, this.Drums[i]]); + drums.push(this.Drums[i]); } - this._dissectNumber.value = inputNum; - - this._rulerSelected = cell.parentNode.getAttribute("data-row"); - this.__dissectByNumber(cell, inputNum, true); - } + // Look for any old entries that we may have missed. + for (let i = 0; i < this._dissectHistory.length; i++) { + let drum = this._dissectHistory[i][1]; + if (drums.indexOf(drum) === -1) { + let history = JSON.parse( + JSON.stringify(this._dissectHistory[i][0]) + ); + dissectHistory.push([history, drum]); + } + } - // this._piemenuRuler(this._rulerSelected); + this._dissectHistory = JSON.parse(JSON.stringify(dissectHistory)); - //Save dissect history everytime user dissects ruler - this.saveDissectHistory(); - }; + this._playing = false; + this._playingOne = false; + this._playingAll = false; + logo.hideMsgs(); - __startTapping (noteValues, interval, event) { - const d = new Date(); - this._tapTimes = [d.getTime()]; - this._tapEndTime = this._tapTimes[0] + interval; + this.widgetWindow.destroy(); + }; - // Set a timeout to end tapping - - setTimeout(() => { - this.__endTapping(event); - }, interval); + this._playAllCell = widgetWindow.addButton( + "play-button.svg", + iconSize, + _("Play all") + ); + this._playAllCell.onclick = () => { + if (this._playing) { + this.__pause(); + } else if (!this._playingAll) { + this.__resume(); + } + }; - // Display a progress bar. - const __move = (tick, stepSize) => { - let width = 1; + this._save_lock = false; + widgetWindow.addButton( + "export-chunk.svg", + iconSize, + _("Save rhythms") + ).onclick = async () => { + // this._save(0); + // Debounce button + if (!this._get_save_lock()) { + this._save_lock = true; + // Save a merged version of the rulers. + this._saveTupletsMerged(this._mergeRulers()); - const id = setInterval(() => { - if (width >= 100) { - clearInterval(id); - } else { - width += stepSize; - this._progressBar.style.width = width + "%"; - } - }, tick); - } + // Rather than each ruler individually. + // this._saveTuplets(0); + await delayExecution(1000); + this._save_lock = false; + } + }; - this._tapCell.innerHTML = "
"; - this._progressBar = this._tapCell.querySelector(".progressBar"); - // Progress once per 8th note. - __move(interval / 8, 100 / 8); - }; + widgetWindow.addButton( + "export-drums.svg", + iconSize, + _("Save drum machine") + ).onclick = async () => { + // Debounce button + if (!this._get_save_lock()) { + this._save_lock = true; + this._saveMachine(0); + await delayExecution(1000); + this._save_lock = false; + } + }; - __endTapping (event) { - const cell = event.target; - if (cell.parentNode === null) { - console.debug("Null parent node in endTapping"); - return; - } + // An input for setting the dissect number + this._dissectNumber = widgetWindow.addInputButton("2"); - this._rulerSelected = cell.parentNode.getAttribute("data-row"); - if (this._progressBar) this._progressBar.remove(); - this._tapCell.innerHTML = ""; + this._dissectNumber.onfocus = (event) => { + // this._piemenuNumber(['2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16'], numberInput.value); + }; - const d = new Date(); - this._tapTimes.push(d.getTime()); - - this._tapMode = false; - if ( - typeof this._rulerSelected === "string" || - typeof this._rulerSelected === "number" - ) { - let noteValues = this.Rulers[this._rulerSelected][0]; - - if (last(this._tapTimes) > this._tapEndTime) { - this._tapTimes[this._tapTimes.length - 1] = this._tapEndTime; - } - - // convert times into cells here. - let inputNum = this._dissectNumber.value; - if (inputNum === "" || isNaN(inputNum)) { - inputNum = 2; - } else { - inputNum = Math.abs(Math.floor(inputNum)); - } - - // Minimum beat is tied to the input number - let minimumBeat; - switch (inputNum) { - case 2: - minimumBeat = 16; - break; - case 3: - minimumBeat = 27; - break; - case 4: - minimumBeat = 32; - break; - case 5: - minimumBeat = 25; - break; - case 6: - minimumBeat = 36; - break; - case 7: - minimumBeat = 14; - break; - case 8: - minimumBeat = 64; - break; - default: - minimumBeat = 16; - break; - } - - const newNoteValues = []; - let sum = 0; - let obj; - // let interval = - // this._bpmFactor / Math.abs(noteValues[this._tapCell.cellIndex]); - for (let i = 1; i < this._tapTimes.length; i++) { - const dtime = this._tapTimes[i] - this._tapTimes[i - 1]; - if (i < this._tapTimes.length - 1) { - obj = nearestBeat( - (100 * dtime) / this._bpmFactor, - minimumBeat - ); - if (obj[0] === 0) { - obj[0] = 1; - obj[1] = obj[1] / 2; - } - - if (sum + obj[0] / obj[1] < 1) { - sum += obj[0] / obj[1]; - newNoteValues.push(obj[1] / obj[0]); - } - } else { - // Since the fractional value is noisy, - // ensure that the final beat make the - // total add up to the proper note value. - obj = rationalToFraction( - 1 / noteValues[this._tapCell.cellIndex] - sum - ); - newNoteValues.push(obj[1] / obj[0]); - } - } - - this.__divideFromList(this._tapCell, newNoteValues, true); - } - - this._tapTimes = []; - this._tapCell = null; - this._tapEndTime = null; - // let iconSize = RhythmRuler.ICONSIZE; - this._tapButton.innerHTML = - '' +
-            _('; - }; - - __addCellEventHandlers (cell, cellWidth, noteValue) { - - - const __mouseOverHandler = (event) => { - const cell = event.target; - if (cell === null || cell.parentNode === null) { - return; - } - - this._rulerSelected = cell.parentNode.getAttribute("data-row"); - let noteValues = this.Rulers[this._rulerSelected][0]; - let noteValue = noteValues[cell.cellIndex]; - let obj; - if (noteValue < 0) { - obj = rationalToFraction( - Math.abs(Math.abs(-1 / noteValue)) - ); - cell.innerHTML = - calcNoteValueToDisplay(obj[1], obj[0], this._cellScale) + - " " + - _("silence"); - } else { - obj = rationalToFraction(Math.abs(Math.abs(1 / noteValue))); - cell.innerHTML = calcNoteValueToDisplay( - obj[1], - obj[0], - this._cellScale + this._dissectNumber.onkeydown = (event) => { + if (event.keyCode === RhythmRuler.DEL) { + this._dissectNumber.value = this._dissectNumber.value.substring( + 0, + this._dissectNumber.value.length - 1 ); } }; - const __mouseOutHandler = (event) => { - const cell = event.target; - cell.innerHTML = ""; - }; - - const __mouseDownHandler = (event) => { - const cell = event.target; - this._mouseDownCell = cell; - - const d = new Date(); - this._longPressStartTime = d.getTime(); - this._inLongPress = false; - - this._longPressBeep = setTimeout(() => { - // Removing audio feedback on long press since it - // occasionally confuses tone.js during rapid clicking - // in the widget. - - // logo.synth.trigger(0, 'C4', 1 / 32, 'chime', null, null); + this._dissectNumber.oninput = (event) => { + // Put a limit on the size (2 <--> 128). + this._dissectNumber.onmouseout = () => { + this._dissectNumber.value = Math.max( + this._dissectNumber.value, + 2 + ); + }; - let cell = this._mouseDownCell; - if (cell !== null && cell.parentNode !== null) { - this._rulerSelected = cell.parentNode.getAttribute( - "data-row" - ); - let noteValues = this.Rulers[this._rulerSelected][0]; - let noteValue = noteValues[cell.cellIndex]; - cell.style.backgroundColor = - platformColor.selectorBackground; - } - }, 1500); + this._dissectNumber.value = Math.max( + Math.min(this._dissectNumber.value, 128), + 2 + ); }; - const __mouseUpHandler = (event) => { - clearTimeout(this._longPressBeep); - const cell = event.target; - this._mouseUpCell = cell; - if (this._mouseDownCell !== this._mouseUpCell) { - this._tieRuler(event, cell.parentNode.getAttribute("data-row")); - } else if (this._longPressStartTime !== null && !this._tapMode) { - const d = new Date(); - const elapseTime = d.getTime() - this._longPressStartTime; - if (elapseTime > 1500) { - this._inLongPress = true; - this.__toggleRestState(this, true); - } - } - - this._mouseDownCell = null; - this._mouseUpCell = null; - this._longPressStartTime = null; + widgetWindow.addButton( + "restore-button.svg", + iconSize, + _("Undo") + ).onclick = () => { + this._undo(); }; - const __clickHandler = (event) => { - if (event == undefined) return; - if (!this.__getLongPressStatus()) { - let cell = event.target; - if (cell !== null && cell.parentNode !== null) { - this._dissectRuler( - event, - cell.parentNode.getAttribute("data-row") - ); - } else { - console.error("Rhythm Ruler: null cell found on click"); - } - } + //.TRANS: user can tap out a rhythm by clicking on a ruler. + this._tapButton = widgetWindow.addButton( + "tap-button.svg", + iconSize, + _("Tap a rhythm") + ); + this._tapButton.onclick = () => { + this._tap(); + }; - this._inLongPress = false; + //.TRANS: clear all subdivisions from the ruler. + widgetWindow.addButton( + "erase-button.svg", + iconSize, + _("Clear") + ).onclick = () => { + this._clear(); }; - let obj; - if (cellWidth > 12 && noteValue > 0) { - obj = rationalToFraction(Math.abs(1 / noteValue)); - cell.innerHTML = calcNoteValueToDisplay( - obj[1], - obj[0], - this._cellScale - ); - } else { - cell.innerHTML = ""; + // We use an outer div to scroll vertically and an inner div to + // scroll horizontally. + let rhythmRulerTable = document.createElement("table"); + widgetWindow.getWidgetBody().append(rhythmRulerTable); - cell.removeEventListener("mouseover", __mouseOverHandler); - cell.addEventListener("mouseover", __mouseOverHandler); + let wMax = 0; + // Each row in the ruler table contains a play button in the + // first column and a ruler table in the second column. + for (let i = 0; i < this.Rulers.length; i++) { + let rhythmRulerTableRow = rhythmRulerTable.insertRow(); - cell.removeEventListener("mouseout", __mouseOutHandler); - cell.addEventListener("mouseout", __mouseOutHandler); - } + if (beginnerMode) { + let w = 0; + for (let r = 0; r < this.Rulers[i][0].length; r++) { + w += 580 / this.Rulers[i][0][r]; + } - cell.removeEventListener("mousedown", __mouseDownHandler); - cell.addEventListener("mousedown", __mouseDownHandler); - - cell.removeEventListener("mouseup", __mouseUpHandler); - cell.addEventListener("mouseup", __mouseUpHandler); - - cell.removeEventListener("click", __clickHandler); - cell.addEventListener("click", __clickHandler); - }; - - __getLongPressStatus () { - return this._inLongPress; - }; - - __toggleRestState (cell, addToUndoList) { - - - if (cell !== null && cell.parentNode !== null) { - this._rulerSelected = cell.parentNode.getAttribute("data-row"); - let noteValues = this.Rulers[this._rulerSelected][0]; - let noteValue = noteValues[cell.cellIndex]; - - const __mouseOverHandler = (event) => { - let cell = event.target; - if (cell === null) { - return; + if (w > wMax) { + rhythmRulerTable.style.width = w + "px"; + wMax = w; } + } else { + let drumcell = rhythmRulerTableRow.insertCell(); + drumcell.innerHTML = + '' +
+                    _('; + drumcell.className = "headcol"; // Position fixed when scrolling horizontally - let obj; + drumcell.onclick = ((id) => { + return () => { + if (this._playing) { + if (this._rulerPlaying === id) { + this.innerHTML = + '' +
+                                    _('; + this._playing = false; + this._playingOne = false; + this._playingAll = false; + this._rulerPlaying = -1; + this._startingTime = null; + this._elapsedTimes[id] = 0; + this._offsets[id] = 0; + setTimeout( + this._calculateZebraStripes(id), + 1000 + ); + } + } else { + if (this._playingOne === false) { + this._rulerSelected = id; + logo.turtleDelay = 0; + this._playing = true; + this._playingOne = true; + this._playingAll = false; + this._cellCounter = 0; + this._startingTime = null; + this._rulerPlaying = id; + this.innerHTML = + '' +
+                                    _('; + this._elapsedTimes[id] = 0; + this._offsets[id] = 0; + this._playOne(); + } + } + }; + })(i); + } - this._rulerSelected = cell.parentNode.getAttribute("data-row"); - let noteValues = this.Rulers[this._rulerSelected][0]; - let noteValue = noteValues[cell.cellIndex]; - if (noteValue < 0) { - obj = rationalToFraction( - Math.abs(Math.abs(-1 / noteValue)) - ); - cell.innerHTML = - calcNoteValueToDisplay( - obj[1], - obj[0], - this._cellScale - ) + - " " + - _("silence"); - } else { - obj = rationalToFraction( - Math.abs(Math.abs(1 / noteValue)) - ); - cell.innerHTML = calcNoteValueToDisplay( - obj[1], - obj[0], - this._cellScale - ); - } - }; + let rulerCell = rhythmRulerTableRow.insertCell(); + // Create individual rulers as tables. + rulerCell.innerHTML = + '
'; - const __mouseOutHandler = (event) => { - let cell = event.target; - cell.innerHTML = ""; - }; + let rulerCellTable = docById("rulerCellTable" + i); + rulerCellTable.style.textAlign = "center"; + rulerCellTable.style.border = "0px"; + rulerCellTable.style.borderCollapse = "collapse"; + rulerCellTable.cellSpacing = "0px"; + rulerCellTable.cellPadding = "0px"; + let rulerRow = rulerCellTable.insertRow(); + this._rulers[i] = rulerRow; + rulerRow.setAttribute("data-row", i); - let obj; - if (noteValue < 0) { - obj = rationalToFraction(Math.abs(1 / noteValue)); - cell.innerHTML = calcNoteValueToDisplay( - obj[1], - obj[0], + for (let j = 0; j < this.Rulers[i][0].length; j++) { + let noteValue = this.Rulers[i][0][j]; + let rulerSubCell = rulerRow.insertCell(-1); + rulerSubCell.innerHTML = calcNoteValueToDisplay( + noteValue, + 1, this._cellScale ); - cell.removeEventListener("mouseover", __mouseOverHandler); - cell.removeEventListener("mouseout", __mouseOutHandler); - } else { - cell.innerHTML = ""; - - cell.removeEventListener("mouseover", __mouseOverHandler); - cell.addEventListener("mouseover", __mouseOverHandler); - - cell.removeEventListener("mouseout", __mouseOutHandler); - cell.addEventListener("mouseout", __mouseOutHandler); - } - - noteValues[cell.cellIndex] = -noteValue; - - this._calculateZebraStripes(this._rulerSelected); + rulerSubCell.style.height = RhythmRuler.RULERHEIGHT + "px"; + rulerSubCell.style.minHeight = rulerSubCell.style.height; + rulerSubCell.style.maxHeight = rulerSubCell.style.height; + rulerSubCell.style.width = this._noteWidth(noteValue) + "px"; + rulerSubCell.style.minWidth = rulerSubCell.style.width; + rulerSubCell.style.border = "0px"; + rulerSubCell.border = "0px"; + rulerSubCell.padding = "0px"; + rulerSubCell.style.padding = "0px"; + rulerSubCell.style.lineHeight = 60 + " % "; + if (i % 2 === 0) { + if (j % 2 === 0) { + rulerSubCell.style.backgroundColor = + platformColor.selectorBackground; + } else { + rulerSubCell.style.backgroundColor = + platformColor.selectorSelected; + } + } else { + if (j % 2 === 0) { + rulerSubCell.style.backgroundColor = + platformColor.selectorSelected; + } else { + rulerSubCell.style.backgroundColor = + platformColor.selectorBackground; + } + } - divisionHistory = this.Rulers[this._rulerSelected][1]; - if (addToUndoList) { - this._undoList.push(["rest", this._rulerSelected]); + this.__addCellEventHandlers( + rulerSubCell, + this._noteWidth(noteValue), + noteValue + ); } - divisionHistory.push(cell.cellIndex); - } - - // this._piemenuRuler(this._rulerSelected); - }; - - __divideFromList (cell, newNoteValues, addToUndoList) { - if (typeof cell !== "object") { - return; - } - - if (typeof newNoteValues !== "object") { - return; + // Match the play button height to the ruler height. + rhythmRulerTableRow.cells[0].style.width = RhythmRuler.BUTTONSIZE + "px"; + rhythmRulerTableRow.cells[0].style.minWidth = RhythmRuler.BUTTONSIZE + "px"; + rhythmRulerTableRow.cells[0].style.maxWidth = RhythmRuler.BUTTONSIZE + "px"; + rhythmRulerTableRow.cells[0].style.height = + rulerRow.offsetHeight + "px"; + rhythmRulerTableRow.cells[0].style.minHeight = + rulerRow.offsetHeight + "px"; + rhythmRulerTableRow.cells[0].style.maxHeight = + rulerRow.offsetHeight + "px"; + rhythmRulerTableRow.cells[0].style.verticalAlign = "middle"; } - let ruler = this._rulers[this._rulerSelected]; - const newCellIndex = cell.cellIndex; - - if ( - typeof this._rulerSelected === "string" || - typeof this._rulerSelected === "number" - ) { - let noteValues = this.Rulers[this._rulerSelected][0]; - - let divisionHistory = this.Rulers[this._rulerSelected][1]; - if (addToUndoList) { - this._undoList.push(["tap", this._rulerSelected]); + // Restore dissect history. + let cell; + for (let drum = 0; drum < this.Drums.length; drum++) { + if (this.Drums[i] === null) { + continue; } - divisionHistory.push([newCellIndex, newNoteValues]); + for (let i = 0; i < this._dissectHistory.length; i++) { + if (this._dissectHistory[i][1] !== this.Drums[drum]) { + continue; + } - ruler.deleteCell(newCellIndex); + let rhythmRulerTableRow = this._rulers[drum]; + for (let j = 0; j < this._dissectHistory[i][0].length; j++) { + if (this._dissectHistory[i][0][j] == undefined) { + continue; + } - // let noteValue = noteValues[newCellIndex]; - // let tempwidth = this._noteWidth(newNoteValue); - noteValues.splice(newCellIndex, 1); + this._rulerSelected = drum; - for (let i = 0; i < newNoteValues.length; i++) { - const newCell = ruler.insertCell(newCellIndex + i); - const newNoteValue = newNoteValues[i]; - const newCellWidth = parseFloat(this._noteWidth(newNoteValue)); - noteValues.splice(newCellIndex + i, 0, newNoteValue); - - newCell.style.width = newCellWidth + "px"; - newCell.style.minWidth = newCell.style.width; - newCell.style.height = RhythmRuler.RULERHEIGHT + "px"; - newCell.style.minHeight = newCell.style.height; - newCell.style.maxHeight = newCell.style.height; + if (typeof this._dissectHistory[i][0][j] === "number") { + cell = + rhythmRulerTableRow.cells[ + this._dissectHistory[i][0][j] + ]; + this.__toggleRestState(cell, false); + } else if ( + typeof this._dissectHistory[i][0][j][0] === "number" + ) { + if ( + typeof this._dissectHistory[i][0][j][1] === "number" + ) { + // dissect is [cell, num] + cell = + rhythmRulerTableRow.cells[ + this._dissectHistory[i][0][j][0] + ]; + if (cell != undefined) { + this.__dissectByNumber( + cell, + this._dissectHistory[i][0][j][1], + false + ); + } else { + console.warn( + "Could not find cell to divide. Did the order of the rhythm blocks change?" + ); + } + } else { + // divide is [cell, [values]] + cell = + rhythmRulerTableRow.cells[ + this._dissectHistory[i][0][j][0] + ]; + if (cell != undefined) { + this.__divideFromList( + cell, + this._dissectHistory[i][0][j][1], + false + ); + } + } + } else { + // tie is [[cell, value], [cell, value]...] + let history = this._dissectHistory[i][0][j]; + this._mouseDownCell = + rhythmRulerTableRow.cells[history[0][0]]; + this._mouseUpCell = + rhythmRulerTableRow.cells[last(history)[0]]; + if (this._mouseUpCell != undefined) { + this.__tie(false); + } - this.__addCellEventHandlers( - newCell, - newCellWidth, - newNoteValue - ); + this._mouseDownCell = null; + this._mouseUpCell = null; + } + } } - - this._calculateZebraStripes(this._rulerSelected); } + logo.textMsg(_("Click on the ruler to divide it.")); // this._piemenuRuler(this._rulerSelected); - }; + } - __dissectByNumber (cell, inputNum, addToUndoList) { - if (typeof cell !== "object") { - return; - } + /** + * @private + * @param {number} noteValue + * @returns {void} + */ + _noteWidth(noteValue) { + return Math.floor(EIGHTHNOTEWIDTH * (8 / Math.abs(noteValue)) * 4); + } - if (typeof inputNum !== "number") { - return; + /** + * @private + * @param {number} rulerno + * @returns {void} + */ + _calculateZebraStripes(rulerno) { + let ruler = this._rulers[rulerno]; + let evenColor; + if (this._rulerSelected % 2 === 0) { + evenColor = platformColor.selectorBackground; + } else { + evenColor = platformColor.selectorSelected; } - let ruler = this._rulers[this._rulerSelected]; - const newCellIndex = cell.cellIndex; - - if ( - typeof this._rulerSelected === "string" || - typeof this._rulerSelected === "number" - ) { - let noteValues = this.Rulers[this._rulerSelected][0]; - - let noteValue = noteValues[newCellIndex]; - if (inputNum * noteValue > 256) { - logo.errorMsg( - _("Maximum value of 256 has been exceeded.") - ); - return; - } else { - logo.hideMsgs(); - } - - let divisionHistory = this.Rulers[this._rulerSelected][1]; - if (addToUndoList) { - this._undoList.push(["dissect", this._rulerSelected]); + for (let i = 0; i < ruler.cells.length; i++) { + const newCell = ruler.cells[i]; + newCell.style.border = "2px solid lightgrey"; + newCell.style.borderRadius = "10px"; + if (evenColor === platformColor.selectorBackground) { + if (i % 2 === 0) { + newCell.style.backgroundColor = + platformColor.selectorBackground; + } else { + newCell.style.backgroundColor = + platformColor.selectorSelected; + } } - divisionHistory.push([newCellIndex, inputNum]); - - ruler.deleteCell(newCellIndex); - - let newNoteValue = 0; - - newNoteValue = inputNum * noteValue; - - const tempwidth = this._noteWidth(newNoteValue); - const difference = - parseFloat(this._noteWidth(noteValue)) - - parseFloat(inputNum) * parseFloat(tempwidth); - const newCellWidth = - parseFloat(this._noteWidth(newNoteValue)) + - parseFloat(difference) / inputNum; - noteValues.splice(newCellIndex, 1); - - for (let i = 0; i < inputNum; i++) { - let newCell = ruler.insertCell(newCellIndex + i); - noteValues.splice(newCellIndex + i, 0, newNoteValue); - - newCell.style.width = newCellWidth + "px"; - newCell.style.minWidth = newCell.style.width; - newCell.style.height = RhythmRuler.RULERHEIGHT + "px"; - newCell.style.minHeight = newCell.style.height; - newCell.style.maxHeight = newCell.style.height; - - this.__addCellEventHandlers( - newCell, - newCellWidth, - newNoteValue - ); + if (evenColor === platformColor.selectorSelected) { + if (i % 2 === 0) { + newCell.style.backgroundColor = + platformColor.selectorSelected; + } else { + newCell.style.backgroundColor = + platformColor.selectorBackground; + } } - - this._calculateZebraStripes(this._rulerSelected); } - - // this._piemenuRuler(this._rulerSelected); }; - _tieRuler (event, ruler) { - if (this._playing) { - console.warn("You cannot tie while widget is playing."); - return; - } else if (this._tapMode) { - // If we are tapping, then treat a tie as a tap. - this._dissectRuler(event, ruler); + /** + * @private + * @param {string} ruler + * @param {Event} event - The triggering event. + * @returns {void} + */ + + _dissectRuler(event, ruler) { + const cell = event.target; + if (cell === null) { return; } - // Does this work if there are more than 10 rulers? - let cell = event.target; - if (cell !== null && cell.parentNode !== null) { - this._rulerSelected = cell.parentNode.getAttribute("data-row"); - this.__tie(true); + if (this._tapMode && this._tapTimes.length > 0) { + let d = new Date(); + this._tapTimes.push(d.getTime()); + return; } - // this._piemenuRuler(this._rulerSelected); - }; - - __tie (addToUndoList) { - let ruler = this._rulers[this._rulerSelected]; - - if (this._mouseDownCell === null || this._mouseUpCell === null) { + const cellParent = cell.parentNode; + if (cellParent === null) { return; } - if (this._mouseDownCell === this._mouseUpCell) { + this._rulerSelected = ruler; + if (this._rulerSelected == undefined) { return; } - if ( - typeof this._rulerSelected === "string" || - typeof this._rulerSelected === "number" - ) { - let noteValues = this.Rulers[this._rulerSelected][0]; - - let downCellIndex = this._mouseDownCell.cellIndex; - let upCellIndex = this._mouseUpCell.cellIndex; - - if (downCellIndex === -1 || upCellIndex === -1) { - return; - } + if (this._playing) { + console.warn("You cannot dissect while widget is playing."); + return; + } else if (this._tapMode) { + // Tap a rhythm by clicking in a cell. + if (this._tapCell === null) { + let noteValues = this.Rulers[this._rulerSelected][0]; + this._tapCell = event.target; + if (noteValues[this._tapCell.cellIndex] < 0) { + // Don't allow tapping in rests. + this._tapCell = null; + this._tapMode = false; + this._tapTimes = []; + this._tapEndTime = null; + this._tapButton.innerHTML = + '' +
+                        _('; + return; + } - if (downCellIndex > upCellIndex) { - let tmp = downCellIndex; - downCellIndex = upCellIndex; - upCellIndex = tmp; - tmp = this._mouseDdownCell; - this._mouseDownCell = this._mouseUpCell; - this._mouseUpCell = tmp; - } + this._tapTimes = []; - noteValues = this.Rulers[this._rulerSelected][0]; + // Play a count off before starting tapping. + const interval = this._bpmFactor / Math.abs(noteValues[this._tapCell.cellIndex]); - let divisionHistory = this.Rulers[this._rulerSelected][1]; - if (addToUndoList) { - this._undoList.push(["tie", this._rulerSelected]); - } + let drum; + if (this.Drums[this._rulerSelected] === null) { + drum = "snare drum"; + } else { + let drumBlockNo = logo.blocks.blockList[ + this.Drums[this._rulerSelected] + ].connections[1]; + drum = logo.blocks.blockList[drumBlockNo].value; + } - let history = []; - for (let i = downCellIndex; i < upCellIndex + 1; i++) { - history.push([i, noteValues[i]]); - } - - divisionHistory.push(history); - - let oldNoteValue = noteValues[downCellIndex]; - let noteValue = Math.abs(1 / oldNoteValue); + + // FIXME: Should be based on meter + for (let i = 0; i < 4; i++) { + setTimeout(() => { + logo.synth.trigger( + 0, "C4", Singer.defaultBPMFactor / 16, drum, null, null + ); + }, (interval * i) / 4); + } - // Delete all the cells between down and up except the down - // cell, which we will expand. - for (let i = upCellIndex; i > downCellIndex; i--) { - noteValue += Math.abs(1 / noteValues[i]); - ruler.deleteCell(i); - this.Rulers[this._rulerSelected][0].splice(i, 1); + setTimeout(() => { + this.__startTapping(noteValues, interval); + }, interval); } - - const newCellWidth = this._noteWidth(1 / noteValue); - // Use noteValue of downCell for REST status. - if (oldNoteValue < 0) { - noteValues[downCellIndex] = -1 / noteValue; + } else { + let noteValues = this.Rulers[this._rulerSelected][0]; + let inputNum = this._dissectNumber.value; + if (inputNum === "" || isNaN(inputNum)) { + inputNum = 2; } else { - noteValues[downCellIndex] = 1 / noteValue; + inputNum = Math.abs(Math.floor(inputNum)); } - this._mouseDownCell.style.width = newCellWidth + "px"; - this._mouseDownCell.style.minWidth = this._mouseDownCell.style.width; - this._mouseDownCell.style.height = RhythmRuler.RULERHEIGHT + "px"; - this._mouseDownCell.style.minHeight = this._mouseDownCell.style.height; - this._mouseDownCell.style.maxHeight = this._mouseDownCell.style.height; - - this.__addCellEventHandlers( - this._mouseDownCell, - newCellWidth, - noteValues[downCellIndex] - ); + this._dissectNumber.value = inputNum; - this._calculateZebraStripes(this._rulerSelected); + this._rulerSelected = cell.parentNode.getAttribute("data-row"); + this.__dissectByNumber(cell, inputNum, true); } - }; - _undo () { - // FIXME: Add undo for REST - logo.synth.stop(); - this._startingTime = null; - this._playing = false; - this._playingAll = false; - this._playingOne = false; - this._rulerPlaying = -1; - this._startingTime = null; + // this._piemenuRuler(this._rulerSelected); - if (this._undoList.length === 0) { - return; - } + //Save dissect history everytime user dissects ruler + this.saveDissectHistory(); + } - let obj = this._undoList.pop(); - let lastRuler = obj[1]; - let divisionHistory = this.Rulers[lastRuler][1]; - if (divisionHistory.length === 0) { + /** + * @private + * @param {number} noteValues + * @param {Event} event - The triggering event. + * @param {number} interval + * @returns {void} + */ + __startTapping(noteValues, interval, event) { + const d = new Date(); + this._tapTimes = [d.getTime()]; + this._tapEndTime = this._tapTimes[0] + interval; + + // Set a timeout to end tapping + setTimeout(() => { + this.__endTapping(event); + }, interval); + + // Display a progress bar. + const __move = (tick, stepSize) => { + let width = 1; + + const id = setInterval(() => { + if (width >= 100) { + clearInterval(id); + } else { + width += stepSize; + this._progressBar.style.width = width + "%"; + } + }, tick); + }; + + this._tapCell.innerHTML = "
"; + this._progressBar = this._tapCell.querySelector(".progressBar"); + // Progress once per 8th note. + __move(interval / 8, 100 / 8); + } + /** + * @private + * @param {Event} event - The triggering event. + * @returns {void} + */ + __endTapping(event) { + const cell = event.target; + if (cell.parentNode === null) { + // console.debug("Null parent node in endTapping"); return; } - let ruler = this._rulers[lastRuler]; - let noteValues = this.Rulers[lastRuler][0]; - - if (obj[0] === "dissect") { - let inputNum = divisionHistory[divisionHistory.length - 1][1]; - let newCellIndex = divisionHistory[divisionHistory.length - 1][0]; - let cellWidth = ruler.cells[newCellIndex].style.width; - let newCellWidth = parseFloat(cellWidth) * inputNum; - let oldCellNoteValue = noteValues[newCellIndex]; - let newNoteValue = oldCellNoteValue / inputNum; + this._rulerSelected = cell.parentNode.getAttribute("data-row"); + if (this._progressBar) this._progressBar.remove(); + this._tapCell.innerHTML = ""; - const newCell = ruler.insertCell(newCellIndex); - newCell.style.width = this._noteWidth(newNoteValue) + "px"; - newCell.style.minWidth = newCell.style.width; - newCell.style.height = RhythmRuler.RULERHEIGHT + "px"; - newCell.style.minHeight = newCell.style.height; - newCell.style.maxHeight = newCell.style.height; + const d = new Date(); + this._tapTimes.push(d.getTime()); - newCell.style.backgroundColor = platformColor.selectorBackground; - newCell.innerHTML = calcNoteValueToDisplay( - oldCellNoteValue / inputNum, - 1, - this._cellScale - ); + this._tapMode = false; + if ( + typeof this._rulerSelected === "string" || + typeof this._rulerSelected === "number" + ) { + let noteValues = this.Rulers[this._rulerSelected][0]; - noteValues[newCellIndex] = oldCellNoteValue / inputNum; - noteValues.splice(newCellIndex + 1, inputNum - 1); + if (last(this._tapTimes) > this._tapEndTime) { + this._tapTimes[this._tapTimes.length - 1] = this._tapEndTime; + } - this.__addCellEventHandlers(newCell, newCellWidth, newNoteValue); + // convert times into cells here. + let inputNum = this._dissectNumber.value; + if (inputNum === "" || isNaN(inputNum)) { + inputNum = 2; + } else { + inputNum = Math.abs(Math.floor(inputNum)); + } - for (let i = 0; i < inputNum; i++) { - ruler.deleteCell(newCellIndex + 1); + // Minimum beat is tied to the input number + let minimumBeat; + switch (inputNum) { + case 2: + minimumBeat = 16; + break; + case 3: + minimumBeat = 27; + break; + case 4: + minimumBeat = 32; + break; + case 5: + minimumBeat = 25; + break; + case 6: + minimumBeat = 36; + break; + case 7: + minimumBeat = 14; + break; + case 8: + minimumBeat = 64; + break; + default: + minimumBeat = 16; + break; } - } else if (obj[0] === "tap") { - let newCellIndex = last(divisionHistory)[0]; - let oldNoteValues = last(divisionHistory)[1]; - // Calculate the new note value based on the sum of the - // oldnoteValues. - let oldCellNoteValue = noteValues[newCellIndex]; + const newNoteValues = []; let sum = 0; - for (let i = 0; i < oldNoteValues.length; i++) { - sum += 1 / oldNoteValues[i]; + let obj; + // let interval = + // this._bpmFactor / Math.abs(noteValues[this._tapCell.cellIndex]); + for (let i = 1; i < this._tapTimes.length; i++) { + const dtime = this._tapTimes[i] - this._tapTimes[i - 1]; + if (i < this._tapTimes.length - 1) { + obj = nearestBeat( + (100 * dtime) / this._bpmFactor, + minimumBeat + ); + if (obj[0] === 0) { + obj[0] = 1; + obj[1] = obj[1] / 2; + } + + if (sum + obj[0] / obj[1] < 1) { + sum += obj[0] / obj[1]; + newNoteValues.push(obj[1] / obj[0]); + } + } else { + // Since the fractional value is noisy, + // ensure that the final beat make the + // total add up to the proper note value. + obj = rationalToFraction( + 1 / noteValues[this._tapCell.cellIndex] - sum + ); + newNoteValues.push(obj[1] / obj[0]); + } } - let newNoteValue = 1 / sum; - let newCellWidth = this._noteWidth(newNoteValue); + this.__divideFromList(this._tapCell, newNoteValues, true); + } - const newCell = ruler.insertCell(newCellIndex); - newCell.style.width = newCellWidth + "px"; - newCell.style.minWidth = newCell.style.width; - newCell.style.height = RhythmRuler.RULERHEIGHT + "px"; - newCell.style.minHeight = newCell.style.height; - newCell.style.maxHeight = newCell.style.height; + this._tapTimes = []; + this._tapCell = null; + this._tapEndTime = null; + // let iconSize = RhythmRuler.ICONSIZE; + this._tapButton.innerHTML = + '' +
+            _('; + } - newCell.style.backgroundColor = platformColor.selectorBackground; + /** + * @private + * @param {HTMLElement} cell - The HTML element target + * @param {number} cellWidth + * @param {number} noteValue + * @returns {void} + */ - let obj = rationalToFraction(newNoteValue); - newCell.innerHTML = calcNoteValueToDisplay( - obj[1], - obj[0], - this._cellScale - ); + __addCellEventHandlers(cell, cellWidth, noteValue) { + - noteValues[newCellIndex] = newNoteValue; - noteValues.splice(newCellIndex + 1, oldNoteValues.length - 1); + const __mouseOverHandler = (event) => { + const cell = event.target; + if (cell === null || cell.parentNode === null) { + return; + } - this.__addCellEventHandlers(newCell, newCellWidth, newNoteValue); - - for (let i = 0; i < oldNoteValues.length; i++) { - ruler.deleteCell(newCellIndex + 1); + this._rulerSelected = cell.parentNode.getAttribute("data-row"); + let noteValues = this.Rulers[this._rulerSelected][0]; + let noteValue = noteValues[cell.cellIndex]; + let obj; + if (noteValue < 0) { + obj = rationalToFraction( + Math.abs(Math.abs(-1 / noteValue)) + ); + cell.innerHTML = + calcNoteValueToDisplay(obj[1], obj[0], this._cellScale) + + " " + + _("silence"); + } else { + obj = rationalToFraction(Math.abs(Math.abs(1 / noteValue))); + cell.innerHTML = calcNoteValueToDisplay( + obj[1], + obj[0], + this._cellScale + ); } - } else if (obj[0] === "tie") { - let history = last(divisionHistory); - // The old cell is the same as the first entry in the - // history. Dissect the old cell into history.length - // parts and restore their size and note values. - if (history.length > 0) { - let oldCell = ruler.cells[history[0][0]]; - let oldCellWidth = this._noteWidth(history[0][1]); - oldCell.style.width = oldCellWidth + "px"; - oldCell.style.minWidth = oldCell.style.width; - oldCell.style.height = RhythmRuler.RULERHEIGHT + "px"; - oldCell.style.minHeight = oldCell.style.height; - oldCell.style.maxHeight = oldCell.style.height; + }; - noteValues[history[0][0]] = history[0][1]; - this.__addCellEventHandlers( - oldCell, - oldCellWidth, - history[0][1] - ); + const __mouseOutHandler = (event) => { + const cell = event.target; + cell.innerHTML = ""; + }; - for (let i = 1; i < history.length; i++) { - const newCell = ruler.insertCell(history[0][0] + i); - let newCellWidth = this._noteWidth(history[i][1]); - newCell.style.width = newCellWidth + "px"; - newCell.style.minWidth = newCell.style.width; - newCell.style.height = RhythmRuler.RULERHEIGHT + "px"; - newCell.style.minHeight = newCell.style.height; - newCell.style.maxHeight = newCell.style.height; + const __mouseDownHandler = (event) => { + const cell = event.target; + this._mouseDownCell = cell; - noteValues.splice(history[0][0] + i, 0, history[i][1]); - newCell.innerHTML = calcNoteValueToDisplay( - history[i][1], - 1, - this._cellScale - ); + const d = new Date(); + this._longPressStartTime = d.getTime(); + this._inLongPress = false; - this.__addCellEventHandlers( - newCell, - newCellWidth, - history[i][1] + this._longPressBeep = setTimeout(() => { + // Removing audio feedback on long press since it + // occasionally confuses tone.js during rapid clicking + // in the widget. + + // logo.synth.trigger(0, 'C4', 1 / 32, 'chime', null, null); + + const cell = this._mouseDownCell; + if (cell !== null && cell.parentNode !== null) { + this._rulerSelected = cell.parentNode.getAttribute( + "data-row" ); + let noteValues = this.Rulers[this._rulerSelected][0]; + let noteValue = noteValues[cell.cellIndex]; + cell.style.backgroundColor = + platformColor.selectorBackground; } + }, 1500); + }; - this.Rulers[lastRuler][0] = noteValues; - } else { - console.warn("empty history encountered... skipping undo"); + const __mouseUpHandler = (event) => { + clearTimeout(this._longPressBeep); + const cell = event.target; + this._mouseUpCell = cell; + if (this._mouseDownCell !== this._mouseUpCell) { + this._tieRuler(event, cell.parentNode.getAttribute("data-row")); + } else if (this._longPressStartTime !== null && !this._tapMode) { + const d = new Date(); + const elapseTime = d.getTime() - this._longPressStartTime; + if (elapseTime > 1500) { + this._inLongPress = true; + this.__toggleRestState(this, true); + } } - } else if (obj[0] === "rest") { - let newCellIndex = last(divisionHistory); - const cell = ruler.cells[newCellIndex]; - this.__toggleRestState(cell, false); - divisionHistory.pop(); - } - divisionHistory.pop(); - this._calculateZebraStripes(lastRuler); + this._mouseDownCell = null; + this._mouseUpCell = null; + this._longPressStartTime = null; + }; - // this._piemenuRuler(this._rulerSelected); - }; + const __clickHandler = (event) => { + if (event == undefined) return; + if (!this.__getLongPressStatus()) { + const cell = event.target; + if (cell !== null && cell.parentNode !== null) { + this._dissectRuler( + event, + cell.parentNode.getAttribute("data-row") + ); + } else { + console.error("Rhythm Ruler: null cell found on click"); + } + } - _tap () { - this._tapMode = true; - let iconSize = RhythmRuler.ICONSIZE; - this._tapButton.innerHTML = - '' +
-            _('; - }; + this._inLongPress = false; + }; - _clear () { - logo.synth.stop(); - logo.resetSynth(0); - this._playing = false; - this._playingAll = false; - this._playingOne = false; - this._rulerPlaying = -1; - this._startingTime = null; - this._playAllCell.innerHTML = - '' +
-            _('; - for (r = 0; r < this.Rulers.length; r++) { - this._rulerSelected = r; - while (this.Rulers[r][1].length > 0) { - this._undo(); - } + let obj; + if (cellWidth > 12 && noteValue > 0) { + obj = rationalToFraction(Math.abs(1 / noteValue)); + cell.innerHTML = calcNoteValueToDisplay( + obj[1], + obj[0], + this._cellScale + ); + } else { + cell.innerHTML = ""; + + cell.removeEventListener("mouseover", __mouseOverHandler); + cell.addEventListener("mouseover", __mouseOverHandler); + + cell.removeEventListener("mouseout", __mouseOutHandler); + cell.addEventListener("mouseout", __mouseOutHandler); } - // this._piemenuRuler(this._rulerSelected); - }; + cell.removeEventListener("mousedown", __mouseDownHandler); + cell.addEventListener("mousedown", __mouseDownHandler); - __pause () { + cell.removeEventListener("mouseup", __mouseUpHandler); + cell.addEventListener("mouseup", __mouseUpHandler); - this._playAllCell.innerHTML = - '' +
-            _('; - this._playing = false; - this._playingAll = false; - this._playingOne = false; - this._rulerPlaying = -1; - this._startingTime = null; - for (let i = 0; i < this.Rulers.length; i++) { - this._calculateZebraStripes(i); - } - }; + cell.removeEventListener("click", __clickHandler); + cell.addEventListener("click", __clickHandler); + } - playAll () { - // External call from run button. - if (this._playing) { - if (this._playingAll) { - this.__pause(); - // Wait for pause to complete before restarting. - this._playingAll = true; - - setTimeout(() => { - this.__resume(); - }, 1000); - } - } else if (!this._playingAll) { - this.__resume(); - } + /** + * @private + * @returns {void} + */ + + + __getLongPressStatus () { + return this._inLongPress; }; - __resume () { + /** + * @private + * @param {number} cellWidth + * @param {boolean} addToUndoList + * @returns {void} + */ - this._playAllCell.innerHTML = - '' +
-            _('; - logo.turtleDelay = 0; - this._playingAll = true; - this._playing = true; - this._playingOne = false; - this._cellCounter = 0; - this._rulerPlaying = -1; - for (let i = 0; i < this.Rulers.length; i++) { - this._elapsedTimes[i] = 0; - this._offsets[i] = 0; - } + __toggleRestState(cell, addToUndoList) { + - this._playAll(); - }; + if (cell !== null && cell.parentNode !== null) { + this._rulerSelected = cell.parentNode.getAttribute("data-row"); + let noteValues = this.Rulers[this._rulerSelected][0]; + let noteValue = noteValues[cell.cellIndex]; - _playAll () { - logo.synth.stop(); - logo.resetSynth(0); - if (this._startingTime === null) { - let d = new Date(); - this._startingTime = d.getTime(); - for (let i = 0; i < this.Rulers.length; i++) { - this._offsets[i] = 0; - this._elapsedTimes[i] = 0; + const __mouseOverHandler = (event) => { + const cell = event.target; + if (cell === null) { + return; + } + + let obj; + + this._rulerSelected = cell.parentNode.getAttribute("data-row"); + let noteValues = this.Rulers[this._rulerSelected][0]; + let noteValue = noteValues[cell.cellIndex]; + if (noteValue < 0) { + obj = rationalToFraction( + Math.abs(Math.abs(-1 / noteValue)) + ); + cell.innerHTML = + calcNoteValueToDisplay( + obj[1], + obj[0], + this._cellScale + ) + + " " + + _("silence"); + } else { + obj = rationalToFraction( + Math.abs(Math.abs(1 / noteValue)) + ); + cell.innerHTML = calcNoteValueToDisplay( + obj[1], + obj[0], + this._cellScale + ); + } + }; + + const __mouseOutHandler = (event) => { + const cell = event.target; + cell.innerHTML = ""; + }; + + let obj; + if (noteValue < 0) { + obj = rationalToFraction(Math.abs(1 / noteValue)); + cell.innerHTML = calcNoteValueToDisplay( + obj[1], + obj[0], + this._cellScale + ); + cell.removeEventListener("mouseover", __mouseOverHandler); + cell.removeEventListener("mouseout", __mouseOutHandler); + } else { + cell.innerHTML = ""; + + cell.removeEventListener("mouseover", __mouseOverHandler); + cell.addEventListener("mouseover", __mouseOverHandler); + + cell.removeEventListener("mouseout", __mouseOutHandler); + cell.addEventListener("mouseout", __mouseOutHandler); } - } - for (let i = 0; i < this.Rulers.length; i++) { - this.__loop(0, i, 0); - } - }; + noteValues[cell.cellIndex] = -noteValue; - _playOne () { - logo.synth.stop(); - logo.resetSynth(0); - if (this._startingTime === null) { - const d = new Date(); - this._startingTime = d.getTime(); - this._elapsedTimes[this._rulerSelected] = 0; - this._offsets[this._rulerSelected] = 0; + this._calculateZebraStripes(this._rulerSelected); + + divisionHistory = this.Rulers[this._rulerSelected][1]; + if (addToUndoList) { + this._undoList.push(["rest", this._rulerSelected]); + } + + divisionHistory.push(cell.cellIndex); } - console.debug("this._rulerSelected " + this._rulerSelected); - this.__loop(0, this._rulerSelected, 0); - }; + // this._piemenuRuler(this._rulerSelected); + } - __loop (noteTime, rulerNo, colIndex) { - let ruler = this._rulers[rulerNo]; - if (ruler === null) { - console.warn("Cannot find ruler " + rulerNo + ". Widget closed?"); + /** + * @private + * @param {HTMLElement} cell - The HTML element target + * @param {number} newNoteValues + * @param {boolean} addToUndoList + * @returns {void} + */ + + __divideFromList(cell, newNoteValues, addToUndoList) { + if (typeof cell !== "object") { return; } - // Refresh the divisions each time we cycle. - if (colIndex === 0) { - this._calculateZebraStripes(rulerNo); + if (typeof newNoteValues !== "object") { + return; } - const cell = ruler.cells[colIndex]; - let noteValues = this.Rulers[rulerNo][0]; - let noteValue = noteValues[colIndex]; + let ruler = this._rulers[this._rulerSelected]; + const newCellIndex = cell.cellIndex; - noteTime = Math.abs(1 / noteValue); - let drum; - if (this.Drums[rulerNo] === null) { - drum = "snare drum"; - } else { - let drumblockno = logo.blocks.blockList[this.Drums[rulerNo]] - .connections[1]; - drum = logo.blocks.blockList[drumblockno].value; - } + if ( + typeof this._rulerSelected === "string" || + typeof this._rulerSelected === "number" + ) { + let noteValues = this.Rulers[this._rulerSelected][0]; - let foundDrum = false; - // Convert i18n drum name to English. - for (let d = 0; d < DRUMNAMES.length; d++) { - if (DRUMNAMES[d][0].replace("-", " ") === drum) { - drum = DRUMNAMES[d][1]; - foundDrum = true; - break; - } else if (DRUMNAMES[d][1] === drum) { - foundDrum = true; - break; + let divisionHistory = this.Rulers[this._rulerSelected][1]; + if (addToUndoList) { + this._undoList.push(["tap", this._rulerSelected]); } - } - let foundVoice = false; - if (!foundDrum) { - for (let d = 0; d < VOICENAMES.length; d++) { - if (VOICENAMES[d][0] === drum) { - drum = VOICENAMES[d][1]; - foundVoice = true; - break; - } else if (VOICENAMES[d][1] === drum) { - foundVoice = true; - break; - } + divisionHistory.push([newCellIndex, newNoteValues]); + + ruler.deleteCell(newCellIndex); + + // let noteValue = noteValues[newCellIndex]; + // let tempwidth = this._noteWidth(newNoteValue); + noteValues.splice(newCellIndex, 1); + + for (let i = 0; i < newNoteValues.length; i++) { + const newCell = ruler.insertCell(newCellIndex + i); + const newNoteValue = newNoteValues[i]; + const newCellWidth = parseFloat(this._noteWidth(newNoteValue)); + noteValues.splice(newCellIndex + i, 0, newNoteValue); + + newCell.style.width = newCellWidth + "px"; + newCell.style.minWidth = newCell.style.width; + newCell.style.height = RhythmRuler.RULERHEIGHT + "px"; + newCell.style.minHeight = newCell.style.height; + newCell.style.maxHeight = newCell.style.height; + + this.__addCellEventHandlers( + newCell, + newCellWidth, + newNoteValue + ); } + + this._calculateZebraStripes(this._rulerSelected); } - + // this._piemenuRuler(this._rulerSelected); + } - if (this._playing) { - // Play the current note. - if (noteValue > 0) { - if (foundVoice) { - logo.synth.trigger( - 0, "C4", Singer.defaultBPMFactor / noteValue, drum, null, null, false - ); - } else if (foundDrum) { - logo.synth.trigger( - 0, ["C4"], Singer.defaultBPMFactor / noteValue, drum, null, null - ); - } - } + /** + * @private + * @param {HTMLElement} cell - The HTML element target + * @param {number} inputNum + * @param {boolean} addToUndoList + * @returns {void} + */ - // And highlight its cell. - cell.style.backgroundColor = platformColor.rulerHighlight; // selectorBackground; + __dissectByNumber(cell, inputNum, addToUndoList) { + if (typeof cell !== "object") { + return; + } - // Calculate any offset in playback. - let d = new Date(); - this._offsets[rulerNo] = - d.getTime() - this._startingTime - this._elapsedTimes[rulerNo]; + if (typeof inputNum !== "number") { + return; } - setTimeout(() => { - colIndex += 1; - if (colIndex === noteValues.length) { - colIndex = 0; + let ruler = this._rulers[this._rulerSelected]; + const newCellIndex = cell.cellIndex; + + if ( + typeof this._rulerSelected === "string" || + typeof this._rulerSelected === "number" + ) { + let noteValues = this.Rulers[this._rulerSelected][0]; + + let noteValue = noteValues[newCellIndex]; + if (inputNum * noteValue > 256) { + logo.errorMsg( + _("Maximum value of 256 has been exceeded.") + ); + return; + } else { + logo.hideMsgs(); } - if (this._playing) { - this.__loop(noteTime, rulerNo, colIndex); + let divisionHistory = this.Rulers[this._rulerSelected][1]; + if (addToUndoList) { + this._undoList.push(["dissect", this._rulerSelected]); } - }, Singer.defaultBPMFactor * 1000 * noteTime - this._offsets[rulerNo]); - this._elapsedTimes[rulerNo] += Singer.defaultBPMFactor * 1000 * noteTime; - }; + divisionHistory.push([newCellIndex, inputNum]); - _save (selectedRuler) { - // Deprecated -- replaced by save tuplets code - - for (let name in logo.blocks.palettes.dict) { - logo.blocks.palettes.dict[name].hideMenu(true); - } + ruler.deleteCell(newCellIndex); - logo.refreshCanvas(); + let newNoteValue = 0; - setTimeout(() => { - let ruler = this._rulers[selectedRuler]; - let noteValues = this.Rulers[selectedRuler][0]; - // Get the first word of drum's name (ignore the word 'drum' itself) - // and add 'rhythm'. - let stack_value; - if (this.Drums[selectedRuler] === null) { - stack_value = _("snare drum") + " " + _("rhythm"); - } else { - stack_value = - logo.blocks.blockList[ - logo.blocks.blockList[this.Drums[selectedRuler]] - .connections[1] - ].value.split(" ")[0] + - " " + - _("rhythm"); - } - let delta = selectedRuler * 42; - let newStack = [ - [ - 0, - ["action", { collapsed: true }], - 100 + delta, - 100 + delta, - [null, 1, 2, null] - ], - [1, ["text", { value: stack_value }], 0, 0, [0]] - ]; - let previousBlock = 0; - let sameNoteValue = 1; - for (let i = 0; i < ruler.cells.length; i++) { - if ( - noteValues[i] === noteValues[i + 1] && - i < ruler.cells.length - 1 - ) { - sameNoteValue += 1; - continue; - } else { - let idx = newStack.length; - let noteValue = noteValues[i]; + newNoteValue = inputNum * noteValue; - let obj = rationalToFraction(1 / Math.abs(noteValue)); + const tempwidth = this._noteWidth(newNoteValue); + const difference = + parseFloat(this._noteWidth(noteValue)) - + parseFloat(inputNum) * parseFloat(tempwidth); + const newCellWidth = + parseFloat(this._noteWidth(newNoteValue)) + + parseFloat(difference) / inputNum; + noteValues.splice(newCellIndex, 1); - newStack.push([ - idx, - "rhythm2", - 0, - 0, - [previousBlock, idx + 1, idx + 2, idx + 5] - ]); - newStack.push([ - idx + 1, - ["number", { value: sameNoteValue }], - 0, - 0, - [idx] - ]); - newStack.push([ - idx + 2, - "divide", - 0, - 0, - [idx, idx + 3, idx + 4] - ]); - newStack.push([ - idx + 3, - ["number", { value: obj[0] }], - 0, - 0, - [idx + 2] - ]); - newStack.push([ - idx + 4, - ["number", { value: obj[1] }], - 0, - 0, - [idx + 2] - ]); - newStack.push([idx + 5, "vspace", 0, 0, [idx, idx + 6]]); - if (i == ruler.cells.length - 1) { - newStack.push([ - idx + 6, - "hidden", - 0, - 0, - [idx + 5, null] - ]); - } else { - newStack.push([ - idx + 6, - "hidden", - 0, - 0, - [idx + 5, idx + 7] - ]); - } + for (let i = 0; i < inputNum; i++) { + let newCell = ruler.insertCell(newCellIndex + i); + noteValues.splice(newCellIndex + i, 0, newNoteValue); - previousBlock = idx + 6; - sameNoteValue = 1; - } + newCell.style.width = newCellWidth + "px"; + newCell.style.minWidth = newCell.style.width; + newCell.style.height = RhythmRuler.RULERHEIGHT + "px"; + newCell.style.minHeight = newCell.style.height; + newCell.style.maxHeight = newCell.style.height; + + this.__addCellEventHandlers( + newCell, + newCellWidth, + newNoteValue + ); } - logo.blocks.loadNewBlocks(newStack); - if (selectedRuler > this.Rulers.length - 2) { + this._calculateZebraStripes(this._rulerSelected); + } + + // this._piemenuRuler(this._rulerSelected); + } + + + /** + * @private + * @param {Event} event - The triggering event. + * @param {string} ruler + * @returns {void} + */ + + _tieRuler(event, ruler) { + if (this._playing) { + console.warn("You cannot tie while widget is playing."); + return; + } else if (this._tapMode) { + // If we are tapping, then treat a tie as a tap. + this._dissectRuler(event, ruler); + return; + } + + // Does this work if there are more than 10 rulers? + const cell = event.target; + if (cell !== null && cell.parentNode !== null) { + this._rulerSelected = cell.parentNode.getAttribute("data-row"); + this.__tie(true); + } + + // this._piemenuRuler(this._rulerSelected); + } + + /** + * @private + * @param {boolean} addToUndoList + * @returns {void} + */ + + __tie(addToUndoList) { + let ruler = this._rulers[this._rulerSelected]; + + if (this._mouseDownCell === null || this._mouseUpCell === null) { + return; + } + + if (this._mouseDownCell === this._mouseUpCell) { + return; + } + + if ( + typeof this._rulerSelected === "string" || + typeof this._rulerSelected === "number" + ) { + let noteValues = this.Rulers[this._rulerSelected][0]; + + let downCellIndex = this._mouseDownCell.cellIndex; + let upCellIndex = this._mouseUpCell.cellIndex; + + if (downCellIndex === -1 || upCellIndex === -1) { return; - } else { - this._save(selectedRuler + 1); } - }, 500); - }; - _saveTuplets (selectedRuler) { - - for (let name in logo.blocks.palettes.dict) { - logo.blocks.palettes.dict[name].hideMenu(true); - } + if (downCellIndex > upCellIndex) { + let tmp = downCellIndex; + downCellIndex = upCellIndex; + upCellIndex = tmp; + tmp = this._mouseDdownCell; + this._mouseDownCell = this._mouseUpCell; + this._mouseUpCell = tmp; + } - logo.refreshCanvas(); + noteValues = this.Rulers[this._rulerSelected][0]; - setTimeout(() => { - let ruler = this._rulers[selectedRuler]; - let noteValues = this.Rulers[selectedRuler][0]; - let stack_value; - if (this.Drums[selectedRuler] === null) { - stack_value = _("rhythm"); + let divisionHistory = this.Rulers[this._rulerSelected][1]; + if (addToUndoList) { + this._undoList.push(["tie", this._rulerSelected]); + } + + let history = []; + for (let i = downCellIndex; i < upCellIndex + 1; i++) { + history.push([i, noteValues[i]]); + } + + divisionHistory.push(history); + + let oldNoteValue = noteValues[downCellIndex]; + let noteValue = Math.abs(1 / oldNoteValue); + + // Delete all the cells between down and up except the down + // cell, which we will expand. + for (let i = upCellIndex; i > downCellIndex; i--) { + noteValue += Math.abs(1 / noteValues[i]); + ruler.deleteCell(i); + this.Rulers[this._rulerSelected][0].splice(i, 1); + } + + const newCellWidth = this._noteWidth(1 / noteValue); + // Use noteValue of downCell for REST status. + if (oldNoteValue < 0) { + noteValues[downCellIndex] = -1 / noteValue; } else { - stack_value = - logo.blocks.blockList[ - logo.blocks.blockList[this.Drums[selectedRuler]] - .connections[1] - ].value.split(" ")[0] + - " " + - _("rhythm"); + noteValues[downCellIndex] = 1 / noteValue; } - let delta = selectedRuler * 42; - let newStack = [ - [ - 0, - ["action", { collapsed: true }], - 100 + delta, - 100 + delta, - [null, 1, 2, null] - ], - [1, ["text", { value: stack_value }], 0, 0, [0]] - ]; - let previousBlock = 0; - let sameNoteValue = 1; - for (let i = 0; i < ruler.cells.length; i++) { - if ( - noteValues[i] === noteValues[i + 1] && - i < ruler.cells.length - 1 - ) { - sameNoteValue += 1; - continue; - } else { - let idx = newStack.length; - let noteValue = noteValues[i]; - let obj = rationalToFraction(1 / Math.abs(noteValue)); - let n = obj[1] / sameNoteValue; - if (Number.isInteger(n)) { - newStack.push([ - idx, - "stuplet", - 0, - 0, - [previousBlock, idx + 1, idx + 2, idx + 5] - ]); - newStack.push([ - idx + 1, - ["number", { value: sameNoteValue }], - 0, - 0, - [idx] - ]); - newStack.push([ - idx + 2, - "divide", - 0, - 0, - [idx, idx + 3, idx + 4] - ]); - newStack.push([ - idx + 3, - ["number", { value: obj[0] }], - 0, - 0, - [idx + 2] - ]); - newStack.push([ - idx + 4, - ["number", { value: n }], - 0, - 0, - [idx + 2] - ]); - newStack.push([ - idx + 5, - "vspace", - 0, - 0, - [idx, idx + 6] - ]); - } else { - newStack.push([ - idx, - "rhythm2", - 0, - 0, - [previousBlock, idx + 1, idx + 2, idx + 5] - ]); - newStack.push([ - idx + 1, - ["number", { value: sameNoteValue }], - 0, - 0, - [idx] - ]); - newStack.push([ - idx + 2, - "divide", - 0, - 0, - [idx, idx + 3, idx + 4] - ]); - newStack.push([ - idx + 3, - ["number", { value: obj[0] }], - 0, - 0, - [idx + 2] - ]); - newStack.push([ - idx + 4, - ["number", { value: obj[1] }], - 0, - 0, - [idx + 2] - ]); - newStack.push([ - idx + 5, - "vspace", - 0, - 0, - [idx, idx + 6] - ]); - } - - if (i == ruler.cells.length - 1) { - newStack.push([ - idx + 6, - "hidden", - 0, - 0, - [idx + 5, null] - ]); - } else { - newStack.push([ - idx + 6, - "hidden", - 0, - 0, - [idx + 5, idx + 7] - ]); - } - previousBlock = idx + 6; - sameNoteValue = 1; - } - } + this._mouseDownCell.style.width = newCellWidth + "px"; + this._mouseDownCell.style.minWidth = this._mouseDownCell.style.width; + this._mouseDownCell.style.height = RhythmRuler.RULERHEIGHT + "px"; + this._mouseDownCell.style.minHeight = this._mouseDownCell.style.height; + this._mouseDownCell.style.maxHeight = this._mouseDownCell.style.height; - logo.blocks.loadNewBlocks(newStack); - if (selectedRuler > this.Rulers.length - 2) { - return; - } else { - this._saveTuplets(selectedRuler + 1); - } - }, 500); - }; + this.__addCellEventHandlers( + this._mouseDownCell, + newCellWidth, + noteValues[downCellIndex] + ); - _saveTupletsMerged (noteValues) { - - for (let name in logo.blocks.palettes.dict) { - logo.blocks.palettes.dict[name].hideMenu(true); + this._calculateZebraStripes(this._rulerSelected); } + } - logo.refreshCanvas(); - - let stack_value = _("rhythm"); - let delta = 42; - let newStack = [ - [ - 0, - ["action", { collapsed: true }], - 100 + delta, - 100 + delta, - [null, 1, 2, null] - ], - [1, ["text", { value: stack_value }], 0, 0, [0]] - ]; - let previousBlock = 0; - let sameNoteValue = 1; - for (let i = 0; i < noteValues.length; i++) { - if ( - noteValues[i] === noteValues[i + 1] && - i < noteValues.length - 1 - ) { - sameNoteValue += 1; - continue; - } else { - let idx = newStack.length; - let noteValue = noteValues[i]; - let obj = rationalToFraction(1 / Math.abs(noteValue)); - newStack.push([ - idx, - "rhythm2", - 0, - 0, - [previousBlock, idx + 1, idx + 2, idx + 5] - ]); - newStack.push([ - idx + 1, - ["number", { value: sameNoteValue }], - 0, - 0, - [idx] - ]); - newStack.push([ - idx + 2, - "divide", - 0, - 0, - [idx, idx + 3, idx + 4] - ]); - newStack.push([ - idx + 3, - ["number", { value: obj[0] }], - 0, - 0, - [idx + 2] - ]); - newStack.push([ - idx + 4, - ["number", { value: obj[1] }], - 0, - 0, - [idx + 2] - ]); - newStack.push([idx + 5, "vspace", 0, 0, [idx, idx + 6]]); + /** + * @private + * @returns {void} + */ - if (i == noteValues.length - 1) { - newStack.push([idx + 6, "hidden", 0, 0, [idx + 5, null]]); - } else { - newStack.push([ - idx + 6, - "hidden", - 0, - 0, - [idx + 5, idx + 7] - ]); - } + _undo () { + // FIXME: Add undo for REST + logo.synth.stop(); + this._startingTime = null; + this._playing = false; + this._playingAll = false; + this._playingOne = false; + this._rulerPlaying = -1; + this._startingTime = null; - previousBlock = idx + 6; - sameNoteValue = 1; - } + if (this._undoList.length === 0) { + return; } - logo.blocks.loadNewBlocks(newStack); - logo.textMsg(_("New action block generated!")); - }; - - _saveMachine (selectedRuler) { - // We are either saving a drum machine or a voice machine. - let drum; - if (this.Drums[selectedRuler] === null) { - drum = "snare drum"; - } else { - let drumBlockNo = logo.blocks.blockList[ - this.Drums[selectedRuler] - ].connections[1]; - drum = logo.blocks.blockList[drumBlockNo].value; + let obj = this._undoList.pop(); + let lastRuler = obj[1]; + let divisionHistory = this.Rulers[lastRuler][1]; + if (divisionHistory.length === 0) { + return; } - for (let d = 0; d < DRUMNAMES.length; d++) { - if (DRUMNAMES[d][1] === drum) { - if (EFFECTSNAMES.indexOf(drum) === -1) { - this._saveDrumMachine(selectedRuler, drum, false); - } else { - this._saveDrumMachine(selectedRuler, drum, true); - } + let ruler = this._rulers[lastRuler]; + let noteValues = this.Rulers[lastRuler][0]; - return; - } - } + if (obj[0] === "dissect") { + let inputNum = divisionHistory[divisionHistory.length - 1][1]; + let newCellIndex = divisionHistory[divisionHistory.length - 1][0]; + let cellWidth = ruler.cells[newCellIndex].style.width; + let newCellWidth = parseFloat(cellWidth) * inputNum; + let oldCellNoteValue = noteValues[newCellIndex]; + let newNoteValue = oldCellNoteValue / inputNum; - for (let d = 0; d < VOICENAMES.length; d++) { - if (VOICENAMES[d][1] === drum) { - this._saveVoiceMachine(selectedRuler, drum); - return; - } - } - }; + const newCell = ruler.insertCell(newCellIndex); + newCell.style.width = this._noteWidth(newNoteValue) + "px"; + newCell.style.minWidth = newCell.style.width; + newCell.style.height = RhythmRuler.RULERHEIGHT + "px"; + newCell.style.minHeight = newCell.style.height; + newCell.style.maxHeight = newCell.style.height; - _saveDrumMachine (selectedRuler, drum, effect) { - - for (let name in logo.blocks.palettes.dict) { - logo.blocks.palettes.dict[name].hideMenu(true); - } + newCell.style.backgroundColor = platformColor.selectorBackground; + newCell.innerHTML = calcNoteValueToDisplay( + oldCellNoteValue / inputNum, + 1, + this._cellScale + ); - logo.refreshCanvas(); + noteValues[newCellIndex] = oldCellNoteValue / inputNum; + noteValues.splice(newCellIndex + 1, inputNum - 1); - setTimeout(() => { - let ruler = this._rulers[selectedRuler]; - let noteValues = this.Rulers[selectedRuler][0]; - let delta = selectedRuler * 42; + this.__addCellEventHandlers(newCell, newCellWidth, newNoteValue); - // Just save the action, not the drum machine itself. - // let newStack = [[0, ['start', {'collapsed': false}], 100 + delta, 100 + delta, [null, 1, null]]]; - // newStack.push([1, 'forever', 0, 0, [0, 2, null]]); - let action_name; - if (this.Drums[selectedRuler] === null) { - action_name = _("snare drum") + " " + _("action"); + for (let i = 0; i < inputNum; i++) { + ruler.deleteCell(newCellIndex + 1); + } + } else if (obj[0] === "tap") { + let newCellIndex = last(divisionHistory)[0]; + let oldNoteValues = last(divisionHistory)[1]; + + // Calculate the new note value based on the sum of the + // oldnoteValues. + let oldCellNoteValue = noteValues[newCellIndex]; + let sum = 0; + for (let i = 0; i < oldNoteValues.length; i++) { + sum += 1 / oldNoteValues[i]; + } + + let newNoteValue = 1 / sum; + let newCellWidth = this._noteWidth(newNoteValue); + + const newCell = ruler.insertCell(newCellIndex); + newCell.style.width = newCellWidth + "px"; + newCell.style.minWidth = newCell.style.width; + newCell.style.height = RhythmRuler.RULERHEIGHT + "px"; + newCell.style.minHeight = newCell.style.height; + newCell.style.maxHeight = newCell.style.height; + + newCell.style.backgroundColor = platformColor.selectorBackground; + + let obj = rationalToFraction(newNoteValue); + newCell.innerHTML = calcNoteValueToDisplay( + obj[1], + obj[0], + this._cellScale + ); + + noteValues[newCellIndex] = newNoteValue; + noteValues.splice(newCellIndex + 1, oldNoteValues.length - 1); + + this.__addCellEventHandlers(newCell, newCellWidth, newNoteValue); + + for (let i = 0; i < oldNoteValues.length; i++) { + ruler.deleteCell(newCellIndex + 1); + } + } else if (obj[0] === "tie") { + let history = last(divisionHistory); + // The old cell is the same as the first entry in the + // history. Dissect the old cell into history.length + // parts and restore their size and note values. + if (history.length > 0) { + let oldCell = ruler.cells[history[0][0]]; + let oldCellWidth = this._noteWidth(history[0][1]); + oldCell.style.width = oldCellWidth + "px"; + oldCell.style.minWidth = oldCell.style.width; + oldCell.style.height = RhythmRuler.RULERHEIGHT + "px"; + oldCell.style.minHeight = oldCell.style.height; + oldCell.style.maxHeight = oldCell.style.height; + + noteValues[history[0][0]] = history[0][1]; + this.__addCellEventHandlers( + oldCell, + oldCellWidth, + history[0][1] + ); + + for (let i = 1; i < history.length; i++) { + const newCell = ruler.insertCell(history[0][0] + i); + let newCellWidth = this._noteWidth(history[i][1]); + newCell.style.width = newCellWidth + "px"; + newCell.style.minWidth = newCell.style.width; + newCell.style.height = RhythmRuler.RULERHEIGHT + "px"; + newCell.style.minHeight = newCell.style.height; + newCell.style.maxHeight = newCell.style.height; + + noteValues.splice(history[0][0] + i, 0, history[i][1]); + newCell.innerHTML = calcNoteValueToDisplay( + history[i][1], + 1, + this._cellScale + ); + + this.__addCellEventHandlers( + newCell, + newCellWidth, + history[i][1] + ); + } + + this.Rulers[lastRuler][0] = noteValues; } else { - action_name = - logo.blocks.blockList[ - logo.blocks.blockList[this.Drums[selectedRuler]] - .connections[1] - ].value.split(" ")[0] + - " " + - _("action"); + console.warn("empty history encountered... skipping undo"); } + } else if (obj[0] === "rest") { + let newCellIndex = last(divisionHistory); + const cell = ruler.cells[newCellIndex]; + this.__toggleRestState(cell, false); + divisionHistory.pop(); + } - let newStack = [ - [ - 0, - ["action", { collapsed: true }], - 100 + delta, - 100 + delta, - [null, 1, 2, null] - ], - [1, ["text", { value: action_name }], 0, 0, [0]] - ]; - let previousBlock = 0; // 1 - let sameNoteValue = 1; - for (let i = 0; i < ruler.cells.length; i++) { - if ( - noteValues[i] === noteValues[i + 1] && - i < ruler.cells.length - 1 - ) { - sameNoteValue += 1; - continue; - } else { - let idx = newStack.length; - let noteValue = noteValues[i]; + divisionHistory.pop(); + this._calculateZebraStripes(lastRuler); - let obj = rationalToFraction(1 / Math.abs(noteValue)); + // this._piemenuRuler(this._rulerSelected); + } - if (sameNoteValue === 1) { - // Add a note block. - newStack.push([ - idx, - "newnote", - 0, - 0, - [previousBlock, idx + 1, idx + 4, idx + 7] - ]); - newStack.push([ - idx + 1, - "divide", - 0, - 0, - [idx, idx + 2, idx + 3] - ]); - newStack.push([ - idx + 2, - ["number", { value: obj[0] }], - 0, - 0, - [idx + 1] - ]); - if (noteValue < 0) { - newStack.push([ - idx + 3, - ["number", { value: obj[1] }], - 0, - 0, - [idx + 1] - ]); - newStack.push([ - idx + 4, - "vspace", - 0, - 0, - [idx, idx + 5] - ]); - newStack.push([ - idx + 5, - "rest2", - 0, - 0, - [idx + 4, idx + 6] - ]); - newStack.push([ - idx + 6, - "hidden", - 0, - 0, - [idx + 5, null] - ]); - } else { - newStack.push([ - idx + 3, - ["number", { value: obj[1] }], - 0, - 0, - [idx + 1] - ]); - newStack.push([ - idx + 4, - "vspace", - 0, - 0, - [idx, idx + 5] - ]); - newStack.push([ - idx + 5, - "playdrum", - 0, - 0, - [idx + 4, idx + 6, null] - ]); - if (effect) { - newStack.push([ - idx + 6, - ["effectsname", { value: drum }], - 0, - 0, - [idx + 5] - ]); - } else { - newStack.push([ - idx + 6, - ["drumname", { value: drum }], - 0, - 0, - [idx + 5] - ]); - } - } - if (i == ruler.cells.length - 1) { - newStack.push([ - idx + 7, - "hidden", - 0, - 0, - [idx, null] - ]); - } else { - newStack.push([ - idx + 7, - "hidden", - 0, - 0, - [idx, idx + 8] - ]); - previousBlock = idx + 7; - } - } else { - // Add a note block inside a repeat block. - if (i == ruler.cells.length - 1) { - newStack.push([ - idx, - "repeat", - 0, - 0, - [previousBlock, idx + 1, idx + 2, null] - ]); - } else { - newStack.push([ - idx, - "repeat", - 0, - 0, - [previousBlock, idx + 1, idx + 2, idx + 10] - ]); - previousBlock = idx; - } - newStack.push([ - idx + 1, - ["number", { value: sameNoteValue }], - 0, - 0, - [idx] - ]); - newStack.push([ - idx + 2, - "newnote", - 0, - 0, - [idx, idx + 3, idx + 6, idx + 9] - ]); - newStack.push([ - idx + 3, - "divide", - 0, - 0, - [idx + 2, idx + 4, idx + 5] - ]); - newStack.push([ - idx + 4, - ["number", { value: 1 }], - 0, - 0, - [idx + 3] - ]); - if (noteValue < 0) { - newStack.push([ - idx + 5, - ["number", { value: -noteValue }], - 0, - 0, - [idx + 3] - ]); - newStack.push([ - idx + 6, - "vspace", - 0, - 0, - [idx + 2, idx + 7] - ]); - newStack.push([ - idx + 7, - "rest2", - 0, - 0, - [idx + 6, idx + 8] - ]); - newStack.push([ - idx + 8, - "hidden", - 0, - 0, - [idx + 7, null] - ]); - } else { - newStack.push([ - idx + 5, - ["number", { value: noteValue }], - 0, - 0, - [idx + 3] - ]); - newStack.push([ - idx + 6, - "vspace", - 0, - 0, - [idx + 2, idx + 7] - ]); - newStack.push([ - idx + 7, - "playdrum", - 0, - 0, - [idx + 6, idx + 8, null] - ]); - if (effect) { - newStack.push([ - idx + 8, - ["effectsname", { value: drum }], - 0, - 0, - [idx + 7] - ]); - } else { - newStack.push([ - idx + 8, - ["drumname", { value: drum }], - 0, - 0, - [idx + 7] - ]); - } - } - newStack.push([ - idx + 9, - "hidden", - 0, - 0, - [idx + 2, null] - ]); - } - sameNoteValue = 1; - } - } + /** + * @private + * @returns {void} + */ - logo.blocks.loadNewBlocks(newStack); - logo.textMsg(_("New action block generated!")); - if (selectedRuler > this.Rulers.length - 2) { - return; - } else { - this._saveMachine(selectedRuler + 1); - } - }, 500); - }; + _tap() { + this._tapMode = true; + let iconSize = RhythmRuler.ICONSIZE; + this._tapButton.innerHTML = + '' +
+            _('; + } - _saveVoiceMachine (selectedRuler, voice) { - - for (const name in logo.blocks.palettes.dict) { - logo.blocks.palettes.dict[name].hideMenu(true); - } + /** + * @private + * @returns {void} + */ - logo.refreshCanvas(); + _clear() { + logo.synth.stop(); + logo.resetSynth(0); + this._playing = false; + this._playingAll = false; + this._playingOne = false; + this._rulerPlaying = -1; + this._startingTime = null; + this._playAllCell.innerHTML = + '' +
+            _('; + for (let r = 0; r < this.Rulers.length; r++) { + this._rulerSelected = r; + while (this.Rulers[r][1].length > 0) { + this._undo(); + } + } - setTimeout(() => { - let ruler = this._rulers[selectedRuler]; - let noteValues = this.Rulers[selectedRuler][0]; - let delta = selectedRuler * 42; + // this._piemenuRuler(this._rulerSelected); + } - // Just save the action, not the drum machine itself. - // let newStack = [[0, ['start', {'collapsed': false}], 100 + delta, 100 + delta, [null, 1, null]]]; - // newStack.push([1, 'settimbre', 0, 0, [0, 2, 4, 3]]); - // newStack.push([2, ['voicename', {'value': voice}], 0, 0, [1]]); - // newStack.push([3, 'hidden', 0, 0, [1, null]]); - // newStack.push([4, 'forever', 0, 0, [1, 6, 5]]); - // newStack.push([5, 'hidden', 0, 0, [4, null]]); + /** + * @private + * @returns {void} + */ - // This should never happen. - let action_name; - if (this.Drums[selectedRuler] === null) { - action_name = _("guitar") + " " + _("action"); - } else { - action_name = - logo.blocks.blockList[ - logo.blocks.blockList[this.Drums[selectedRuler]] - .connections[1] - ].value.split(" ")[0] + - "_" + - _("action"); - } + __pause() { - let newStack = [ - [ - 0, - ["action", { collapsed: true }], - 100 + delta, - 100 + delta, - [null, 1, 2, null] - ], - [1, ["text", { value: action_name }], 0, 0, [0]] - ]; - newStack.push([2, "settimbre", 0, 0, [0, 3, 5, 4]]); - newStack.push([3, ["voicename", { value: voice }], 0, 0, [2]]); - newStack.push([4, "hidden", 0, 0, [2, null]]); - let previousBlock = 2; - let sameNoteValue = 1; - for (let i = 0; i < ruler.cells.length; i++) { - if ( - noteValues[i] === noteValues[i + 1] && - i < ruler.cells.length - 1 - ) { - sameNoteValue += 1; - continue; - } else { - let idx = newStack.length; - let noteValue = noteValues[i]; + this._playAllCell.innerHTML = + '' +
+            _('; + this._playing = false; + this._playingAll = false; + this._playingOne = false; + this._rulerPlaying = -1; + this._startingTime = null; + for (let i = 0; i < this.Rulers.length; i++) { + this._calculateZebraStripes(i); + } + } - let obj = rationalToFraction(1 / Math.abs(noteValue)); + /** + * @public + * @returns {void} + */ - if (sameNoteValue === 1) { - // Add a note block. - if (noteValue < 0) { - newStack.push([ - idx, - "newnote", - 0, - 0, - [previousBlock, idx + 1, idx + 4, idx + 7] - ]); - newStack.push([ - idx + 1, - "divide", - 0, - 0, - [idx, idx + 2, idx + 3] - ]); - newStack.push([ - idx + 2, - ["number", { value: obj[0] }], - 0, - 0, - [idx + 1] - ]); + playAll() { + // External call from run button. + if (this._playing) { + if (this._playingAll) { + this.__pause(); + // Wait for pause to complete before restarting. + this._playingAll = true; + + setTimeout(() => { + this.__resume(); + }, 1000); + } + } else if (!this._playingAll) { + this.__resume(); + } + } + + /** + * @private + * @returns {void} + */ + + __resume() { + + this._playAllCell.innerHTML = + '' +
+            _('; + logo.turtleDelay = 0; + this._playingAll = true; + this._playing = true; + this._playingOne = false; + this._cellCounter = 0; + this._rulerPlaying = -1; + for (let i = 0; i < this.Rulers.length; i++) { + this._elapsedTimes[i] = 0; + this._offsets[i] = 0; + } + + this._playAll(); + } + + /** + * @private + * @returns {void} + */ + + _playAll() { + logo.synth.stop(); + logo.resetSynth(0); + if (this._startingTime === null) { + let d = new Date(); + this._startingTime = d.getTime(); + for (let i = 0; i < this.Rulers.length; i++) { + this._offsets[i] = 0; + this._elapsedTimes[i] = 0; + } + } + + for (let i = 0; i < this.Rulers.length; i++) { + this.__loop(0, i, 0); + } + } + + /** + * @private + * @returns {void} + */ + + _playOne() { + logo.synth.stop(); + logo.resetSynth(0); + if (this._startingTime === null) { + const d = new Date(); + this._startingTime = d.getTime(); + this._elapsedTimes[this._rulerSelected] = 0; + this._offsets[this._rulerSelected] = 0; + } + console.debug("this._rulerSelected " + this._rulerSelected); + + this.__loop(0, this._rulerSelected, 0); + } + + /** + * @private + * @param {number} noteTime + * @param {number} rulerNo + * @param {number} colIndex + * @returns {void} + */ + + __loop(noteTime, rulerNo, colIndex) { + let ruler = this._rulers[rulerNo]; + if (ruler === null) { + console.warn("Cannot find ruler " + rulerNo + ". Widget closed?"); + return; + } + + // Refresh the divisions each time we cycle. + if (colIndex === 0) { + this._calculateZebraStripes(rulerNo); + } + + const cell = ruler.cells[colIndex]; + let noteValues = this.Rulers[rulerNo][0]; + let noteValue = noteValues[colIndex]; + + noteTime = Math.abs(1 / noteValue); + let drum; + if (this.Drums[rulerNo] === null) { + drum = "snare drum"; + } else { + let drumblockno = logo.blocks.blockList[this.Drums[rulerNo]] + .connections[1]; + drum = logo.blocks.blockList[drumblockno].value; + } + + let foundDrum = false; + // Convert i18n drum name to English. + for (let d = 0; d < DRUMNAMES.length; d++) { + if (DRUMNAMES[d][0].replace("-", " ") === drum) { + drum = DRUMNAMES[d][1]; + foundDrum = true; + break; + } else if (DRUMNAMES[d][1] === drum) { + foundDrum = true; + break; + } + } + + let foundVoice = false; + if (!foundDrum) { + for (let d = 0; d < VOICENAMES.length; d++) { + if (VOICENAMES[d][0] === drum) { + drum = VOICENAMES[d][1]; + foundVoice = true; + break; + } else if (VOICENAMES[d][1] === drum) { + foundVoice = true; + break; + } + } + } + + + + if (this._playing) { + // Play the current note. + if (noteValue > 0) { + if (foundVoice) { + logo.synth.trigger( + 0, "C4", Singer.defaultBPMFactor / noteValue, drum, null, null, false + ); + } else if (foundDrum) { + logo.synth.trigger( + 0, ["C4"], Singer.defaultBPMFactor / noteValue, drum, null, null + ); + } + } + + // And highlight its cell. + cell.style.backgroundColor = platformColor.rulerHighlight; // selectorBackground; + + // Calculate any offset in playback. + let d = new Date(); + this._offsets[rulerNo] = + d.getTime() - this._startingTime - this._elapsedTimes[rulerNo]; + } + + setTimeout(() => { + colIndex += 1; + if (colIndex === noteValues.length) { + colIndex = 0; + } + + if (this._playing) { + this.__loop(noteTime, rulerNo, colIndex); + } + }, Singer.defaultBPMFactor * 1000 * noteTime - this._offsets[rulerNo]); + + this._elapsedTimes[rulerNo] += Singer.defaultBPMFactor * 1000 * noteTime; + } + + /** + * @deprecated + * @private + * @param {number} selectedRuler + * @returns {void} + */ + + _save(selectedRuler) { + // Deprecated -- replaced by save tuplets code + + for (let name in logo.blocks.palettes.dict) { + logo.blocks.palettes.dict[name].hideMenu(true); + } + + logo.refreshCanvas(); + + setTimeout(() => { + let ruler = this._rulers[selectedRuler]; + let noteValues = this.Rulers[selectedRuler][0]; + // Get the first word of drum's name (ignore the word 'drum' itself) + // and add 'rhythm'. + let stack_value; + if (this.Drums[selectedRuler] === null) { + stack_value = _("snare drum") + " " + _("rhythm"); + } else { + stack_value = + logo.blocks.blockList[ + logo.blocks.blockList[this.Drums[selectedRuler]] + .connections[1] + ].value.split(" ")[0] + + " " + + _("rhythm"); + } + let delta = selectedRuler * 42; + let newStack = [ + [ + 0, + ["action", { collapsed: true }], + 100 + delta, + 100 + delta, + [null, 1, 2, null] + ], + [1, ["text", { value: stack_value }], 0, 0, [0]] + ]; + let previousBlock = 0; + let sameNoteValue = 1; + for (let i = 0; i < ruler.cells.length; i++) { + if ( + noteValues[i] === noteValues[i + 1] && + i < ruler.cells.length - 1 + ) { + sameNoteValue += 1; + continue; + } else { + let idx = newStack.length; + let noteValue = noteValues[i]; + + let obj = rationalToFraction(1 / Math.abs(noteValue)); + + newStack.push([ + idx, + "rhythm2", + 0, + 0, + [previousBlock, idx + 1, idx + 2, idx + 5] + ]); + newStack.push([ + idx + 1, + ["number", { value: sameNoteValue }], + 0, + 0, + [idx] + ]); + newStack.push([ + idx + 2, + "divide", + 0, + 0, + [idx, idx + 3, idx + 4] + ]); + newStack.push([ + idx + 3, + ["number", { value: obj[0] }], + 0, + 0, + [idx + 2] + ]); + newStack.push([ + idx + 4, + ["number", { value: obj[1] }], + 0, + 0, + [idx + 2] + ]); + newStack.push([idx + 5, "vspace", 0, 0, [idx, idx + 6]]); + if (i == ruler.cells.length - 1) { + newStack.push([ + idx + 6, + "hidden", + 0, + 0, + [idx + 5, null] + ]); + } else { + newStack.push([ + idx + 6, + "hidden", + 0, + 0, + [idx + 5, idx + 7] + ]); + } + + previousBlock = idx + 6; + sameNoteValue = 1; + } + } + + logo.blocks.loadNewBlocks(newStack); + if (selectedRuler > this.Rulers.length - 2) { + return; + } else { + this._save(selectedRuler + 1); + } + }, 500); + } + + /** + * @private + * @param {number} selectedRuler + * @returns {void} + */ + + + _saveTuplets(selectedRuler) { + + for (let name in logo.blocks.palettes.dict) { + logo.blocks.palettes.dict[name].hideMenu(true); + } + + logo.refreshCanvas(); + + setTimeout(() => { + let ruler = this._rulers[selectedRuler]; + let noteValues = this.Rulers[selectedRuler][0]; + let stack_value; + if (this.Drums[selectedRuler] === null) { + stack_value = _("rhythm"); + } else { + stack_value = + logo.blocks.blockList[ + logo.blocks.blockList[this.Drums[selectedRuler]] + .connections[1] + ].value.split(" ")[0] + + " " + + _("rhythm"); + } + let delta = selectedRuler * 42; + let newStack = [ + [ + 0, + ["action", { collapsed: true }], + 100 + delta, + 100 + delta, + [null, 1, 2, null] + ], + [1, ["text", { value: stack_value }], 0, 0, [0]] + ]; + let previousBlock = 0; + let sameNoteValue = 1; + for (let i = 0; i < ruler.cells.length; i++) { + if ( + noteValues[i] === noteValues[i + 1] && + i < ruler.cells.length - 1 + ) { + sameNoteValue += 1; + continue; + } else { + let idx = newStack.length; + let noteValue = noteValues[i]; + let obj = rationalToFraction(1 / Math.abs(noteValue)); + let n = obj[1] / sameNoteValue; + if (Number.isInteger(n)) { + newStack.push([ + idx, + "stuplet", + 0, + 0, + [previousBlock, idx + 1, idx + 2, idx + 5] + ]); + newStack.push([ + idx + 1, + ["number", { value: sameNoteValue }], + 0, + 0, + [idx] + ]); + newStack.push([ + idx + 2, + "divide", + 0, + 0, + [idx, idx + 3, idx + 4] + ]); + newStack.push([ + idx + 3, + ["number", { value: obj[0] }], + 0, + 0, + [idx + 2] + ]); + newStack.push([ + idx + 4, + ["number", { value: n }], + 0, + 0, + [idx + 2] + ]); + newStack.push([ + idx + 5, + "vspace", + 0, + 0, + [idx, idx + 6] + ]); + } else { + newStack.push([ + idx, + "rhythm2", + 0, + 0, + [previousBlock, idx + 1, idx + 2, idx + 5] + ]); + newStack.push([ + idx + 1, + ["number", { value: sameNoteValue }], + 0, + 0, + [idx] + ]); + newStack.push([ + idx + 2, + "divide", + 0, + 0, + [idx, idx + 3, idx + 4] + ]); + newStack.push([ + idx + 3, + ["number", { value: obj[0] }], + 0, + 0, + [idx + 2] + ]); + newStack.push([ + idx + 4, + ["number", { value: obj[1] }], + 0, + 0, + [idx + 2] + ]); + newStack.push([ + idx + 5, + "vspace", + 0, + 0, + [idx, idx + 6] + ]); + } + + if (i == ruler.cells.length - 1) { + newStack.push([ + idx + 6, + "hidden", + 0, + 0, + [idx + 5, null] + ]); + } else { + newStack.push([ + idx + 6, + "hidden", + 0, + 0, + [idx + 5, idx + 7] + ]); + } + + previousBlock = idx + 6; + sameNoteValue = 1; + } + } + + logo.blocks.loadNewBlocks(newStack); + if (selectedRuler > this.Rulers.length - 2) { + return; + } else { + this._saveTuplets(selectedRuler + 1); + } + }, 500); + } + + /** + * @private + * @param {number} selectedRuler + * @returns {void} + */ + + _saveTupletsMerged(noteValues) { + + for (let name in logo.blocks.palettes.dict) { + logo.blocks.palettes.dict[name].hideMenu(true); + } + + logo.refreshCanvas(); + + let stack_value = _("rhythm"); + let delta = 42; + let newStack = [ + [ + 0, + ["action", { collapsed: true }], + 100 + delta, + 100 + delta, + [null, 1, 2, null] + ], + [1, ["text", { value: stack_value }], 0, 0, [0]] + ]; + let previousBlock = 0; + let sameNoteValue = 1; + for (let i = 0; i < noteValues.length; i++) { + if ( + noteValues[i] === noteValues[i + 1] && + i < noteValues.length - 1 + ) { + sameNoteValue += 1; + continue; + } else { + let idx = newStack.length; + let noteValue = noteValues[i]; + let obj = rationalToFraction(1 / Math.abs(noteValue)); + newStack.push([ + idx, + "rhythm2", + 0, + 0, + [previousBlock, idx + 1, idx + 2, idx + 5] + ]); + newStack.push([ + idx + 1, + ["number", { value: sameNoteValue }], + 0, + 0, + [idx] + ]); + newStack.push([ + idx + 2, + "divide", + 0, + 0, + [idx, idx + 3, idx + 4] + ]); + newStack.push([ + idx + 3, + ["number", { value: obj[0] }], + 0, + 0, + [idx + 2] + ]); + newStack.push([ + idx + 4, + ["number", { value: obj[1] }], + 0, + 0, + [idx + 2] + ]); + newStack.push([idx + 5, "vspace", 0, 0, [idx, idx + 6]]); + + if (i == noteValues.length - 1) { + newStack.push([idx + 6, "hidden", 0, 0, [idx + 5, null]]); + } else { + newStack.push([ + idx + 6, + "hidden", + 0, + 0, + [idx + 5, idx + 7] + ]); + } + + previousBlock = idx + 6; + sameNoteValue = 1; + } + } + + logo.blocks.loadNewBlocks(newStack); + logo.textMsg(_("New action block generated!")); + } + + /** + * @private + * @param {number} selectedRuler + * @returns {void} + */ + + _saveMachine (selectedRuler) { + // We are either saving a drum machine or a voice machine. + let drum; + if (this.Drums[selectedRuler] === null) { + drum = "snare drum"; + } else { + let drumBlockNo = logo.blocks.blockList[ + this.Drums[selectedRuler] + ].connections[1]; + drum = logo.blocks.blockList[drumBlockNo].value; + } + + for (let d = 0; d < DRUMNAMES.length; d++) { + if (DRUMNAMES[d][1] === drum) { + if (EFFECTSNAMES.indexOf(drum) === -1) { + this._saveDrumMachine(selectedRuler, drum, false); + } else { + this._saveDrumMachine(selectedRuler, drum, true); + } + + return; + } + } + + for (let d = 0; d < VOICENAMES.length; d++) { + if (VOICENAMES[d][1] === drum) { + this._saveVoiceMachine(selectedRuler, drum); + return; + } + } + } + + /** + * @private + * @param {number} selectedRuler + * @param {string} drum + * @param {boolean} effect + * @returns {void} + */ + + _saveDrumMachine(selectedRuler, drum, effect) { + + for (let name in logo.blocks.palettes.dict) { + logo.blocks.palettes.dict[name].hideMenu(true); + } + + logo.refreshCanvas(); + + setTimeout(() => { + let ruler = this._rulers[selectedRuler]; + let noteValues = this.Rulers[selectedRuler][0]; + let delta = selectedRuler * 42; + + // Just save the action, not the drum machine itself. + // let newStack = [[0, ['start', {'collapsed': false}], 100 + delta, 100 + delta, [null, 1, null]]]; + // newStack.push([1, 'forever', 0, 0, [0, 2, null]]); + let action_name; + if (this.Drums[selectedRuler] === null) { + action_name = _("snare drum") + " " + _("action"); + } else { + action_name = + logo.blocks.blockList[ + logo.blocks.blockList[this.Drums[selectedRuler]] + .connections[1] + ].value.split(" ")[0] + + " " + + _("action"); + } + + let newStack = [ + [ + 0, + ["action", { collapsed: true }], + 100 + delta, + 100 + delta, + [null, 1, 2, null] + ], + [1, ["text", { value: action_name }], 0, 0, [0]] + ]; + let previousBlock = 0; // 1 + let sameNoteValue = 1; + for (let i = 0; i < ruler.cells.length; i++) { + if ( + noteValues[i] === noteValues[i + 1] && + i < ruler.cells.length - 1 + ) { + sameNoteValue += 1; + continue; + } else { + let idx = newStack.length; + let noteValue = noteValues[i]; + + let obj = rationalToFraction(1 / Math.abs(noteValue)); + + if (sameNoteValue === 1) { + // Add a note block. + newStack.push([ + idx, + "newnote", + 0, + 0, + [previousBlock, idx + 1, idx + 4, idx + 7] + ]); + newStack.push([ + idx + 1, + "divide", + 0, + 0, + [idx, idx + 2, idx + 3] + ]); + newStack.push([ + idx + 2, + ["number", { value: obj[0] }], + 0, + 0, + [idx + 1] + ]); + if (noteValue < 0) { newStack.push([ idx + 3, ["number", { value: obj[1] }], @@ -2010,100 +2310,64 @@ class RhythmRuler { 0, [idx + 5, null] ]); - if (i == ruler.cells.length - 1) { - newStack.push([ - idx + 7, - "hidden", - 0, - 0, - [idx, null] - ]); - } else { - newStack.push([ - idx + 7, - "hidden", - 0, - 0, - [idx, idx + 8] - ]); - previousBlock = idx + 7; - } } else { newStack.push([ - idx, - "newnote", - 0, - 0, - [previousBlock, idx + 1, idx + 4, idx + 8] - ]); - newStack.push([ - idx + 1, - "divide", - 0, - 0, - [idx, idx + 2, idx + 3] - ]); - newStack.push([ - idx + 2, - ["number", { value: obj[0] }], - 0, - 0, - [idx + 1] - ]); - newStack.push([ - idx + 3, - ["number", { value: obj[1] }], - 0, - 0, - [idx + 1] - ]); - newStack.push([ - idx + 4, - "vspace", - 0, - 0, - [idx, idx + 5] - ]); - newStack.push([ - idx + 5, - "pitch", + idx + 3, + ["number", { value: obj[1] }], 0, 0, - [idx + 4, idx + 6, idx + 7, null] + [idx + 1] ]); newStack.push([ - idx + 6, - ["notename", { value: "C" }], + idx + 4, + "vspace", 0, 0, - [idx + 5] + [idx, idx + 5] ]); newStack.push([ - idx + 7, - ["number", { value: 4 }], + idx + 5, + "playdrum", 0, 0, - [idx + 5] + [idx + 4, idx + 6, null] ]); - if (i == ruler.cells.length - 1) { + if (effect) { newStack.push([ - idx + 8, - "hidden", + idx + 6, + ["effectsname", { value: drum }], 0, 0, - [idx, null] + [idx + 5] ]); } else { newStack.push([ - idx + 8, - "hidden", + idx + 6, + ["drumname", { value: drum }], 0, 0, - [idx, idx + 9] + [idx + 5] ]); - previousBlock = idx + 8; } } + if (i == ruler.cells.length - 1) { + newStack.push([ + idx + 7, + "hidden", + 0, + 0, + [idx, null] + ]); + } else { + newStack.push([ + idx + 7, + "hidden", + 0, + 0, + [idx, idx + 8] + ]); + previousBlock = idx + 7; + } } else { // Add a note block inside a repeat block. if (i == ruler.cells.length - 1) { @@ -2120,7 +2384,7 @@ class RhythmRuler { "repeat", 0, 0, - [previousBlock, idx + 1, idx + 2, idx + 11] + [previousBlock, idx + 1, idx + 2, idx + 10] ]); previousBlock = idx; } @@ -2131,28 +2395,28 @@ class RhythmRuler { 0, [idx] ]); + newStack.push([ + idx + 2, + "newnote", + 0, + 0, + [idx, idx + 3, idx + 6, idx + 9] + ]); + newStack.push([ + idx + 3, + "divide", + 0, + 0, + [idx + 2, idx + 4, idx + 5] + ]); + newStack.push([ + idx + 4, + ["number", { value: 1 }], + 0, + 0, + [idx + 3] + ]); if (noteValue < 0) { - newStack.push([ - idx + 2, - "newnote", - 0, - 0, - [idx, idx + 3, idx + 6, idx + 9] - ]); - newStack.push([ - idx + 3, - "divide", - 0, - 0, - [idx + 2, idx + 4, idx + 5] - ]); - newStack.push([ - idx + 4, - ["number", { value: 1 }], - 0, - 0, - [idx + 3] - ]); newStack.push([ idx + 5, ["number", { value: -noteValue }], @@ -2181,35 +2445,7 @@ class RhythmRuler { 0, [idx + 7, null] ]); - newStack.push([ - idx + 9, - "hidden", - 0, - 0, - [idx + 2, null] - ]); } else { - newStack.push([ - idx + 2, - "newnote", - 0, - 0, - [idx, idx + 3, idx + 6, idx + 10] - ]); - newStack.push([ - idx + 3, - "divide", - 0, - 0, - [idx + 2, idx + 4, idx + 5] - ]); - newStack.push([ - idx + 4, - ["number", { value: 1 }], - 0, - 0, - [idx + 3] - ]); newStack.push([ idx + 5, ["number", { value: noteValue }], @@ -2226,517 +2462,484 @@ class RhythmRuler { ]); newStack.push([ idx + 7, - "pitch", - 0, - 0, - [idx + 6, idx + 8, idx + 9, null] - ]); - newStack.push([ - idx + 8, - ["notename", { value: "C" }], - 0, - 0, - [idx + 7] - ]); - newStack.push([ - idx + 9, - ["number", { value: 4 }], - 0, - 0, - [idx + 7] - ]); - newStack.push([ - idx + 10, - "hidden", + "playdrum", 0, 0, - [idx + 2, null] + [idx + 6, idx + 8, null] ]); - } - } - - sameNoteValue = 1; - } - } - - logo.blocks.loadNewBlocks(newStack); - if (selectedRuler > this.Rulers.length - 2) { - return; - } else { - this._saveMachine(selectedRuler + 1); - } - }, 500); - }; - - _mergeRulers () { - // Merge the rulers into one set of rhythms. - const rList = []; - let noteValues; - for (let r = 0; r < this.Rulers.length; r++) { - let t = 0; - let selectedRuler = this.Rulers[r]; - noteValues = selectedRuler[0]; - for (let i = 0; i < noteValues.length; i++) { - t += 1 / noteValues[i]; - if (rList.indexOf(t) === -1) { - rList.push(t); - } - } - } - - rList.sort((a, b) => { - return a - b; - }); - - noteValues = []; - for (let i = 0; i < rList.length; i++) { - if (i === 0) { - noteValues.push(1 / rList[i]); - } else { - noteValues.push(1 / (rList[i] - rList[i - 1])); - } - } - - return noteValues; - }; - - _get_save_lock () { - return this._save_lock; - }; - - init () { - // console.debug("init RhythmRuler"); - - this._bpmFactor = (1000 * TONEBPM) / Singer.masterBPM; - - this._playing = false; - this._playingOne = false; - this._playingAll = false; - this._rulerPlaying = -1; - this._startingTime = null; - this._expanded = false; - - // If there are no drums, add one. - if (this.Drums.length === 0) { - this.Drums.push(null); - this.Rulers.push([[1], []]); - } - - this._elapsedTimes = []; - this._offsets = []; - for (let i = 0; i < this.Rulers.length; i++) { - this._elapsedTimes.push(0); - this._offsets.push(0); - } - - let w = window.innerWidth; - this._cellScale = 1.0; - let iconSize = RhythmRuler.ICONSIZE; - - let widgetWindow = window.widgetWindows.windowFor(this, "rhythm maker"); - this.widgetWindow = widgetWindow; - widgetWindow.clear(); - widgetWindow.show(); - - // For the button callbacks - - - widgetWindow.onclose = () => { - // If the piemenu was open, close it. - // docById('wheelDiv').style.display = 'none'; - // docById('contextWheelDiv').style.display = 'none'; - - // Save the new dissect history. - let dissectHistory = []; - let drums = []; - for (let i = 0; i < this.Rulers.length; i++) { - if (this.Drums[i] === null) { - continue; - } + if (effect) { + newStack.push([ + idx + 8, + ["effectsname", { value: drum }], + 0, + 0, + [idx + 7] + ]); + } else { + newStack.push([ + idx + 8, + ["drumname", { value: drum }], + 0, + 0, + [idx + 7] + ]); + } + } + newStack.push([ + idx + 9, + "hidden", + 0, + 0, + [idx + 2, null] + ]); + } - let history = []; - for (let j = 0; j < this.Rulers[i][1].length; j++) { - history.push(this.Rulers[i][1][j]); + sameNoteValue = 1; } - - this._dissectNumber.classList.add("hasKeyboard"); - dissectHistory.push([history, this.Drums[i]]); - drums.push(this.Drums[i]); } - // Look for any old entries that we may have missed. - for (let i = 0; i < this._dissectHistory.length; i++) { - let drum = this._dissectHistory[i][1]; - if (drums.indexOf(drum) === -1) { - let history = JSON.parse( - JSON.stringify(this._dissectHistory[i][0]) - ); - dissectHistory.push([history, drum]); - } + logo.blocks.loadNewBlocks(newStack); + logo.textMsg(_("New action block generated!")); + if (selectedRuler > this.Rulers.length - 2) { + return; + } else { + this._saveMachine(selectedRuler + 1); } + }, 500); + } - this._dissectHistory = JSON.parse(JSON.stringify(dissectHistory)); - - this._playing = false; - this._playingOne = false; - this._playingAll = false; - logo.hideMsgs(); - - this.widgetWindow.destroy(); - }; + /** + * @private + * @param {number} selectedRuler + * @param {string} voice + * @returns {void} + */ - this._playAllCell = widgetWindow.addButton( - "play-button.svg", - iconSize, - _("Play all") - ); - this._playAllCell.onclick = () => { - if (this._playing) { - this.__pause(); - } else if (!this._playingAll) { - this.__resume(); - } - }; + _saveVoiceMachine(selectedRuler, voice) { + + for (const name in logo.blocks.palettes.dict) { + logo.blocks.palettes.dict[name].hideMenu(true); + } - this._save_lock = false; - widgetWindow.addButton( - "export-chunk.svg", - iconSize, - _("Save rhythms") - ).onclick = async () => { - // this._save(0); - // Debounce button - if (!this._get_save_lock()) { - this._save_lock = true; + logo.refreshCanvas(); - // Save a merged version of the rulers. - this._saveTupletsMerged(this._mergeRulers()); + setTimeout(() => { + let ruler = this._rulers[selectedRuler]; + let noteValues = this.Rulers[selectedRuler][0]; + let delta = selectedRuler * 42; - // Rather than each ruler individually. - // this._saveTuplets(0); - await delayExecution(1000); - this._save_lock = false; - } - }; + // Just save the action, not the drum machine itself. + // let newStack = [[0, ['start', {'collapsed': false}], 100 + delta, 100 + delta, [null, 1, null]]]; + // newStack.push([1, 'settimbre', 0, 0, [0, 2, 4, 3]]); + // newStack.push([2, ['voicename', {'value': voice}], 0, 0, [1]]); + // newStack.push([3, 'hidden', 0, 0, [1, null]]); + // newStack.push([4, 'forever', 0, 0, [1, 6, 5]]); + // newStack.push([5, 'hidden', 0, 0, [4, null]]); - widgetWindow.addButton( - "export-drums.svg", - iconSize, - _("Save drum machine") - ).onclick = async () => { - // Debounce button - if (!this._get_save_lock()) { - this._save_lock = true; - this._saveMachine(0); - await delayExecution(1000); - this._save_lock = false; + // This should never happen. + let action_name; + if (this.Drums[selectedRuler] === null) { + action_name = _("guitar") + " " + _("action"); + } else { + action_name = + logo.blocks.blockList[ + logo.blocks.blockList[this.Drums[selectedRuler]] + .connections[1] + ].value.split(" ")[0] + + "_" + + _("action"); } - }; - - // An input for setting the dissect number - this._dissectNumber = widgetWindow.addInputButton("2"); - - this._dissectNumber.onfocus = (event) => { - // this._piemenuNumber(['2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16'], numberInput.value); - }; - this._dissectNumber.onkeydown = (event) => { - if (event.keyCode === RhythmRuler.DEL) { - this._dissectNumber.value = this._dissectNumber.value.substring( + let newStack = [ + [ 0, - this._dissectNumber.value.length - 1 - ); - } - }; - - this._dissectNumber.oninput = (event) => { - // Put a limit on the size (2 <--> 128). - this._dissectNumber.onmouseout = () => { - this._dissectNumber.value = Math.max( - this._dissectNumber.value, - 2 - ); - }; - - this._dissectNumber.value = Math.max( - Math.min(this._dissectNumber.value, 128), - 2 - ); - }; - - widgetWindow.addButton( - "restore-button.svg", - iconSize, - _("Undo") - ).onclick = () => { - this._undo(); - }; - - //.TRANS: user can tap out a rhythm by clicking on a ruler. - this._tapButton = widgetWindow.addButton( - "tap-button.svg", - iconSize, - _("Tap a rhythm") - ); - this._tapButton.onclick = () => { - this._tap(); - }; - - //.TRANS: clear all subdivisions from the ruler. - widgetWindow.addButton( - "erase-button.svg", - iconSize, - _("Clear") - ).onclick = () => { - this._clear(); - }; - - // We use an outer div to scroll vertically and an inner div to - // scroll horizontally. - let rhythmRulerTable = document.createElement("table"); - widgetWindow.getWidgetBody().append(rhythmRulerTable); - - let wMax = 0; - // Each row in the ruler table contains a play button in the - // first column and a ruler table in the second column. - for (let i = 0; i < this.Rulers.length; i++) { - let rhythmRulerTableRow = rhythmRulerTable.insertRow(); - - if (beginnerMode) { - let w = 0; - for (let r = 0; r < this.Rulers[i][0].length; r++) { - w += 580 / this.Rulers[i][0][r]; - } - - if (w > wMax) { - rhythmRulerTable.style.width = w + "px"; - wMax = w; - } - } else { - let drumcell = rhythmRulerTableRow.insertCell(); - drumcell.innerHTML = - '' +
-                    _('; - drumcell.className = "headcol"; // Position fixed when scrolling horizontally - - drumcell.onclick = ((id) => { - return () => { - if (this._playing) { - if (this._rulerPlaying === id) { - this.innerHTML = - '' +
-                                    _('; - this._playing = false; - this._playingOne = false; - this._playingAll = false; - this._rulerPlaying = -1; - this._startingTime = null; - this._elapsedTimes[id] = 0; - this._offsets[id] = 0; - setTimeout( - this._calculateZebraStripes(id), - 1000 - ); + ["action", { collapsed: true }], + 100 + delta, + 100 + delta, + [null, 1, 2, null] + ], + [1, ["text", { value: action_name }], 0, 0, [0]] + ]; + newStack.push([2, "settimbre", 0, 0, [0, 3, 5, 4]]); + newStack.push([3, ["voicename", { value: voice }], 0, 0, [2]]); + newStack.push([4, "hidden", 0, 0, [2, null]]); + let previousBlock = 2; + let sameNoteValue = 1; + for (let i = 0; i < ruler.cells.length; i++) { + if ( + noteValues[i] === noteValues[i + 1] && + i < ruler.cells.length - 1 + ) { + sameNoteValue += 1; + continue; + } else { + let idx = newStack.length; + let noteValue = noteValues[i]; + + let obj = rationalToFraction(1 / Math.abs(noteValue)); + + if (sameNoteValue === 1) { + // Add a note block. + if (noteValue < 0) { + newStack.push([ + idx, + "newnote", + 0, + 0, + [previousBlock, idx + 1, idx + 4, idx + 7] + ]); + newStack.push([ + idx + 1, + "divide", + 0, + 0, + [idx, idx + 2, idx + 3] + ]); + newStack.push([ + idx + 2, + ["number", { value: obj[0] }], + 0, + 0, + [idx + 1] + ]); + newStack.push([ + idx + 3, + ["number", { value: obj[1] }], + 0, + 0, + [idx + 1] + ]); + newStack.push([ + idx + 4, + "vspace", + 0, + 0, + [idx, idx + 5] + ]); + newStack.push([ + idx + 5, + "rest2", + 0, + 0, + [idx + 4, idx + 6] + ]); + newStack.push([ + idx + 6, + "hidden", + 0, + 0, + [idx + 5, null] + ]); + if (i == ruler.cells.length - 1) { + newStack.push([ + idx + 7, + "hidden", + 0, + 0, + [idx, null] + ]); + } else { + newStack.push([ + idx + 7, + "hidden", + 0, + 0, + [idx, idx + 8] + ]); + previousBlock = idx + 7; } } else { - if (this._playingOne === false) { - this._rulerSelected = id; - logo.turtleDelay = 0; - this._playing = true; - this._playingOne = true; - this._playingAll = false; - this._cellCounter = 0; - this._startingTime = null; - this._rulerPlaying = id; - this.innerHTML = - '' +
-                                    _('; - this._elapsedTimes[id] = 0; - this._offsets[id] = 0; - this._playOne(); + newStack.push([ + idx, + "newnote", + 0, + 0, + [previousBlock, idx + 1, idx + 4, idx + 8] + ]); + newStack.push([ + idx + 1, + "divide", + 0, + 0, + [idx, idx + 2, idx + 3] + ]); + newStack.push([ + idx + 2, + ["number", { value: obj[0] }], + 0, + 0, + [idx + 1] + ]); + newStack.push([ + idx + 3, + ["number", { value: obj[1] }], + 0, + 0, + [idx + 1] + ]); + newStack.push([ + idx + 4, + "vspace", + 0, + 0, + [idx, idx + 5] + ]); + newStack.push([ + idx + 5, + "pitch", + 0, + 0, + [idx + 4, idx + 6, idx + 7, null] + ]); + newStack.push([ + idx + 6, + ["notename", { value: "C" }], + 0, + 0, + [idx + 5] + ]); + newStack.push([ + idx + 7, + ["number", { value: 4 }], + 0, + 0, + [idx + 5] + ]); + if (i == ruler.cells.length - 1) { + newStack.push([ + idx + 8, + "hidden", + 0, + 0, + [idx, null] + ]); + } else { + newStack.push([ + idx + 8, + "hidden", + 0, + 0, + [idx, idx + 9] + ]); + previousBlock = idx + 8; } } - }; - })(i); - } - - let rulerCell = rhythmRulerTableRow.insertCell(); - // Create individual rulers as tables. - rulerCell.innerHTML = - '
'; - - let rulerCellTable = docById("rulerCellTable" + i); - rulerCellTable.style.textAlign = "center"; - rulerCellTable.style.border = "0px"; - rulerCellTable.style.borderCollapse = "collapse"; - rulerCellTable.cellSpacing = "0px"; - rulerCellTable.cellPadding = "0px"; - let rulerRow = rulerCellTable.insertRow(); - this._rulers[i] = rulerRow; - rulerRow.setAttribute("data-row", i); - - for (let j = 0; j < this.Rulers[i][0].length; j++) { - let noteValue = this.Rulers[i][0][j]; - let rulerSubCell = rulerRow.insertCell(-1); - rulerSubCell.innerHTML = calcNoteValueToDisplay( - noteValue, - 1, - this._cellScale - ); - rulerSubCell.style.height = RhythmRuler.RULERHEIGHT + "px"; - rulerSubCell.style.minHeight = rulerSubCell.style.height; - rulerSubCell.style.maxHeight = rulerSubCell.style.height; - rulerSubCell.style.width = this._noteWidth(noteValue) + "px"; - rulerSubCell.style.minWidth = rulerSubCell.style.width; - rulerSubCell.style.border = "0px"; - rulerSubCell.border = "0px"; - rulerSubCell.padding = "0px"; - rulerSubCell.style.padding = "0px"; - rulerSubCell.style.lineHeight = 60 + " % "; - if (i % 2 === 0) { - if (j % 2 === 0) { - rulerSubCell.style.backgroundColor = - platformColor.selectorBackground; - } else { - rulerSubCell.style.backgroundColor = - platformColor.selectorSelected; - } - } else { - if (j % 2 === 0) { - rulerSubCell.style.backgroundColor = - platformColor.selectorSelected; - } else { - rulerSubCell.style.backgroundColor = - platformColor.selectorBackground; + } else { + // Add a note block inside a repeat block. + if (i == ruler.cells.length - 1) { + newStack.push([ + idx, + "repeat", + 0, + 0, + [previousBlock, idx + 1, idx + 2, null] + ]); + } else { + newStack.push([ + idx, + "repeat", + 0, + 0, + [previousBlock, idx + 1, idx + 2, idx + 11] + ]); + previousBlock = idx; + } + newStack.push([ + idx + 1, + ["number", { value: sameNoteValue }], + 0, + 0, + [idx] + ]); + if (noteValue < 0) { + newStack.push([ + idx + 2, + "newnote", + 0, + 0, + [idx, idx + 3, idx + 6, idx + 9] + ]); + newStack.push([ + idx + 3, + "divide", + 0, + 0, + [idx + 2, idx + 4, idx + 5] + ]); + newStack.push([ + idx + 4, + ["number", { value: 1 }], + 0, + 0, + [idx + 3] + ]); + newStack.push([ + idx + 5, + ["number", { value: -noteValue }], + 0, + 0, + [idx + 3] + ]); + newStack.push([ + idx + 6, + "vspace", + 0, + 0, + [idx + 2, idx + 7] + ]); + newStack.push([ + idx + 7, + "rest2", + 0, + 0, + [idx + 6, idx + 8] + ]); + newStack.push([ + idx + 8, + "hidden", + 0, + 0, + [idx + 7, null] + ]); + newStack.push([ + idx + 9, + "hidden", + 0, + 0, + [idx + 2, null] + ]); + } else { + newStack.push([ + idx + 2, + "newnote", + 0, + 0, + [idx, idx + 3, idx + 6, idx + 10] + ]); + newStack.push([ + idx + 3, + "divide", + 0, + 0, + [idx + 2, idx + 4, idx + 5] + ]); + newStack.push([ + idx + 4, + ["number", { value: 1 }], + 0, + 0, + [idx + 3] + ]); + newStack.push([ + idx + 5, + ["number", { value: noteValue }], + 0, + 0, + [idx + 3] + ]); + newStack.push([ + idx + 6, + "vspace", + 0, + 0, + [idx + 2, idx + 7] + ]); + newStack.push([ + idx + 7, + "pitch", + 0, + 0, + [idx + 6, idx + 8, idx + 9, null] + ]); + newStack.push([ + idx + 8, + ["notename", { value: "C" }], + 0, + 0, + [idx + 7] + ]); + newStack.push([ + idx + 9, + ["number", { value: 4 }], + 0, + 0, + [idx + 7] + ]); + newStack.push([ + idx + 10, + "hidden", + 0, + 0, + [idx + 2, null] + ]); + } } + + sameNoteValue = 1; } + } - this.__addCellEventHandlers( - rulerSubCell, - this._noteWidth(noteValue), - noteValue - ); + logo.blocks.loadNewBlocks(newStack); + if (selectedRuler > this.Rulers.length - 2) { + return; + } else { + this._saveMachine(selectedRuler + 1); } + }, 500); + } - // Match the play button height to the ruler height. - rhythmRulerTableRow.cells[0].style.width = RhythmRuler.BUTTONSIZE + "px"; - rhythmRulerTableRow.cells[0].style.minWidth = RhythmRuler.BUTTONSIZE + "px"; - rhythmRulerTableRow.cells[0].style.maxWidth = RhythmRuler.BUTTONSIZE + "px"; - rhythmRulerTableRow.cells[0].style.height = - rulerRow.offsetHeight + "px"; - rhythmRulerTableRow.cells[0].style.minHeight = - rulerRow.offsetHeight + "px"; - rhythmRulerTableRow.cells[0].style.maxHeight = - rulerRow.offsetHeight + "px"; - rhythmRulerTableRow.cells[0].style.verticalAlign = "middle"; - } + /** + * @private + * @returns {array} + */ - // Restore dissect history. - let cell; - for (let drum = 0; drum < this.Drums.length; drum++) { - if (this.Drums[i] === null) { - continue; + _mergeRulers() { + // Merge the rulers into one set of rhythms. + const rList = []; + let noteValues; + for (let r = 0; r < this.Rulers.length; r++) { + let t = 0; + let selectedRuler = this.Rulers[r]; + noteValues = selectedRuler[0]; + for (let i = 0; i < noteValues.length; i++) { + t += 1 / noteValues[i]; + if (rList.indexOf(t) === -1) { + rList.push(t); + } } + } - for (let i = 0; i < this._dissectHistory.length; i++) { - if (this._dissectHistory[i][1] !== this.Drums[drum]) { - continue; - } + rList.sort((a, b) => { + return a - b; + }); - let rhythmRulerTableRow = this._rulers[drum]; - for (let j = 0; j < this._dissectHistory[i][0].length; j++) { - if (this._dissectHistory[i][0][j] == undefined) { - continue; - } + noteValues = []; + for (let i = 0; i < rList.length; i++) { + if (i === 0) { + noteValues.push(1 / rList[i]); + } else { + noteValues.push(1 / (rList[i] - rList[i - 1])); + } + } - this._rulerSelected = drum; + return noteValues; + } - if (typeof this._dissectHistory[i][0][j] === "number") { - cell = - rhythmRulerTableRow.cells[ - this._dissectHistory[i][0][j] - ]; - this.__toggleRestState(cell, false); - } else if ( - typeof this._dissectHistory[i][0][j][0] === "number" - ) { - if ( - typeof this._dissectHistory[i][0][j][1] === "number" - ) { - // dissect is [cell, num] - cell = - rhythmRulerTableRow.cells[ - this._dissectHistory[i][0][j][0] - ]; - if (cell != undefined) { - this.__dissectByNumber( - cell, - this._dissectHistory[i][0][j][1], - false - ); - } else { - console.warn( - "Could not find cell to divide. Did the order of the rhythm blocks change?" - ); - } - } else { - // divide is [cell, [values]] - cell = - rhythmRulerTableRow.cells[ - this._dissectHistory[i][0][j][0] - ]; - if (cell != undefined) { - this.__divideFromList( - cell, - this._dissectHistory[i][0][j][1], - false - ); - } - } - } else { - // tie is [[cell, value], [cell, value]...] - let history = this._dissectHistory[i][0][j]; - this._mouseDownCell = - rhythmRulerTableRow.cells[history[0][0]]; - this._mouseUpCell = - rhythmRulerTableRow.cells[last(history)[0]]; - if (this._mouseUpCell != undefined) { - this.__tie(false); - } + /** + * @private + * @returns {boolean} + */ - this._mouseDownCell = null; - this._mouseUpCell = null; - } - } - } - } + _get_save_lock() { + return this._save_lock; + } - logo.textMsg(_("Click on the ruler to divide it.")); - // this._piemenuRuler(this._rulerSelected); - }; + /** + * @public + * @returns {void} + */ - saveDissectHistory () { + saveDissectHistory() { // Save the new dissect history. let dissectHistory = []; @@ -2770,9 +2973,9 @@ class RhythmRuler { } this._dissectHistory = JSON.parse(JSON.stringify(dissectHistory)); - }; + } - _piemenuRuler (selectedRuler) { + _piemenuRuler(selectedRuler) { return; // In progress /* // piemenu version of ruler @@ -2819,9 +3022,14 @@ class RhythmRuler { this._wheel.createWheel(); */ - }; + } - _piemenuNumber (wheelValues, selectedValue) { + /** + * @private + * @returns {void} + */ + + _piemenuNumber(wheelValues, selectedValue) { // input form and wheelNav pie menu for number selection docById("wheelDiv").style.display = ""; @@ -2905,9 +3113,15 @@ class RhythmRuler { this._exitWheel.navItems[0].navigateFunction = () => { __exitMenu(); }; - }; + } + + /** + * @private + * @returns {void} + */ + - _positionWheel () { + _positionWheel() { if (docById("wheelDiv").style.display == "none") { return; } @@ -2931,5 +3145,5 @@ class RhythmRuler { } else { docById("wheelDiv").style.top = y - 300 + "px"; } - }; + } } \ No newline at end of file From b9a147c92aec4589b264304b445fe9a4139bb332 Mon Sep 17 00:00:00 2001 From: ricknjacky Date: Sat, 30 Jan 2021 21:28:21 +0530 Subject: [PATCH 5/7] fixing ESLint workflow issues --- js/blocks/WidgetBlocks.js | 81 +++---- js/widgets/rhythmruler.js | 477 +++++++++++++++----------------------- 2 files changed, 234 insertions(+), 324 deletions(-) diff --git a/js/blocks/WidgetBlocks.js b/js/blocks/WidgetBlocks.js index d9a83dfdc7..7d876e5b48 100644 --- a/js/blocks/WidgetBlocks.js +++ b/js/blocks/WidgetBlocks.js @@ -1,3 +1,4 @@ +/* eslint-disable no-undef */ function setupWidgetBlocks() { class EnvelopeBlock extends FlowBlock { constructor() { @@ -23,7 +24,7 @@ function setupWidgetBlocks() { } flow(args, logo, turtle, blk) { - let tur = logo.turtles.ithTurtle(turtle); + const tur = logo.turtles.ithTurtle(turtle); if (args.length === 4 && typeof args[0] === "number") { if (args[0] < 0 || args[0] > 100) { @@ -101,7 +102,7 @@ function setupWidgetBlocks() { let rollOff; if (args.length === 3 && typeof args[1] === "number") { - for (let ftype in FILTERTYPES) { + for (const ftype in FILTERTYPES) { if (FILTERTYPES[ftype][0] === args[0]) { filtertype = FILTERTYPES[ftype][1]; } else if (FILTERTYPES[ftype][1] === args[0]) { @@ -185,31 +186,31 @@ function setupWidgetBlocks() { logo.insideTemperament = true; logo.temperament.inTemperament = args[0]; - let scale = []; + const scale = []; if ( logo.blocks.blockList[logo.blocks.blockList[blk].connections[2]] .name === "pitch" ) { - let pitchBlock = + const pitchBlock = logo.blocks.blockList[ logo.blocks.blockList[blk].connections[2] ]; - let note = + const note = logo.blocks.blockList[pitchBlock.connections[1]].value; - let octave = + const octave = logo.blocks.blockList[pitchBlock.connections[2]].value; - let setKey = logo.blocks.blockList[pitchBlock.connections[3]]; + const setKey = logo.blocks.blockList[pitchBlock.connections[3]]; scale[0] = logo.blocks.blockList[setKey.connections[1]].value; scale[1] = logo.blocks.blockList[setKey.connections[2]].value; logo.synth.startingPitch = note + octave; logo.temperament.scale = scale; } - let listenerName = "_temperament_" + turtle; + const listenerName = "_temperament_" + turtle; logo.setDispatchBlock(blk, turtle, listenerName); - let __listener = function(event) { + const __listener = function(event) { logo.temperament.init(); }; @@ -322,10 +323,10 @@ function setupWidgetBlocks() { logo.timbre.duoSynthParams = []; logo.timbre.notesToPlay = []; - let listenerName = "_timbre_" + turtle; + const listenerName = "_timbre_" + turtle; logo.setDispatchBlock(blk, turtle, listenerName); - let __listener = function(event) { + const __listener = function(event) { logo.timbre.init(); }; @@ -363,10 +364,10 @@ function setupWidgetBlocks() { flow(args, logo, turtle, blk) { logo.insideMeterWidget = true; - let listenerName = "_meterwidget_" + turtle; + const listenerName = "_meterwidget_" + turtle; logo.setDispatchBlock(blk, turtle, listenerName); - let __listener = function(event) { + const __listener = function(event) { logo.meterWidget = new MeterWidget(blk); logo.insideMeterWidget = false; @@ -391,18 +392,19 @@ function setupWidgetBlocks() { "oscilloscope" ]); this.formBlock({ name: _("oscilloscope"), canCollapse: true }); - let addPrintTurtle = (blocks,turtle,prev,last) => { - let len = blocks.length; - let next = last ? null : len+2 + const addPrintTurtle = (blocks,turtle,prev,last) => { + const len = blocks.length; + const next = last ? null : len+2; blocks.push([len, "print", 0, 0, [prev, len + 1, next]]); blocks.push([len + 1, ["text", { value: turtle.name}], 0, 0, [len, null]]); return blocks; - } + }; this.makeMacro((x, y) => { let blocks = [[0,"oscilloscope", x, y, [null, 1, null]]]; - for (let turtle of turtles.turtleList) { + for (const turtle of turtles.turtleList) { if (!turtle.inTrash) + // eslint-disable-next-line max-len blocks = addPrintTurtle(blocks, turtle, Math.max(0, blocks.length - 2), turtle == last(turtles.turtleList)); } blocks[0][4][2]=blocks.length; @@ -415,10 +417,10 @@ function setupWidgetBlocks() { logo.oscilloscopeTurtles = []; logo.inOscilloscope = true; - let listenerName = "_oscilloscope_" + turtle; + const listenerName = "_oscilloscope_" + turtle; logo.setDispatchBlock(blk, turtle, listenerName); - let __listener = function(event) { + const __listener = function(event) { logo.Oscilloscope = new Oscilloscope(logo); logo.inOscilloscope = false; }; @@ -457,10 +459,10 @@ function setupWidgetBlocks() { flow(args, logo, turtle, blk) { logo.insideModeWidget = true; - let listenerName = "_modewidget_" + turtle; + const listenerName = "_modewidget_" + turtle; logo.setDispatchBlock(blk, turtle, listenerName); - let __listener = function(event) { + const __listener = function(event) { logo.modeWidget = new ModeWidget(); logo.insideModeWidget = false; }; @@ -507,10 +509,10 @@ function setupWidgetBlocks() { logo.tempo.BPMBlocks = []; logo.tempo.BPMs = []; - let listenerName = "_tempo_" + turtle; + const listenerName = "_tempo_" + turtle; logo.setDispatchBlock(blk, turtle, listenerName); - let __listener = function(event) { + const __listener = function(event) { logo.tempo.init(); }; @@ -565,10 +567,10 @@ function setupWidgetBlocks() { logo.pitchDrumMatrix.drums = []; logo.pitchDrumMatrix.clearBlocks(); - let listenerName = "_pitchdrummatrix_" + turtle; + const listenerName = "_pitchdrummatrix_" + turtle; logo.setDispatchBlock(blk, turtle, listenerName); - let __listener = function(event) { + const __listener = function(event) { if ( logo.pitchDrumMatrix.drums.length === 0 || logo.pitchDrumMatrix.rowLabels.length === 0 @@ -622,10 +624,10 @@ function setupWidgetBlocks() { logo.inPitchSlider = true; logo.pitchSlider.frequencies = []; - let listenerName = "_pitchslider_" + turtle; + const listenerName = "_pitchslider_" + turtle; logo.setDispatchBlock(blk, turtle, listenerName); - let __listener = function(event) { + const __listener = function(event) { logo.pitchSlider.init(logo); logo.inPitchSlider = false; }; @@ -754,10 +756,10 @@ function setupWidgetBlocks() { logo.musicKeyboard.octaves = []; logo.musicKeyboard._rowBlocks = []; - let listenerName = "_musickeyboard_" + turtle; + const listenerName = "_musickeyboard_" + turtle; logo.setDispatchBlock(blk, turtle, listenerName); - let __listener = function(event) { + const __listener = function(event) { logo.musicKeyboard.init(logo); }; @@ -803,10 +805,10 @@ function setupWidgetBlocks() { logo.inPitchStaircase = true; - let listenerName = "_pitchstaircase_" + turtle; + const listenerName = "_pitchstaircase_" + turtle; logo.setDispatchBlock(blk, turtle, listenerName); - let __listener = function(event) { + const __listener = function(event) { logo.pitchStaircase.init(logo); logo.inPitchStaircase = false; }; @@ -890,10 +892,10 @@ function setupWidgetBlocks() { logo.rhythmRuler.Drums = []; logo.inRhythmRuler = true; - let listenerName = "_rhythmruler_" + turtle; + const listenerName = "_rhythmruler_" + turtle; logo.setDispatchBlock(blk, turtle, listenerName); - let __listener = function(event) { + const __listener = function(event) { logo.rhythmRuler.init(); }; @@ -1033,10 +1035,10 @@ function setupWidgetBlocks() { logo.tupletParams = []; logo.addingNotesToTuplet = false; - let listenerName = "_matrix_" + turtle; + const listenerName = "_matrix_" + turtle; logo.setDispatchBlock(blk, turtle, listenerName); - let __listener = function(event) { + const __listener = function(event) { if ( logo.tupletRhythms.length === 0 || logo.phraseMaker.rowLabels.length === 0 @@ -1061,7 +1063,8 @@ function setupWidgetBlocks() { switch (logo.tupletRhythms[i][0]) { case "notes": case "simple": - let tupletParam = [logo.tupletParams[logo.tupletRhythms[i][1]]]; + // eslint-disable-next-line no-case-declarations + const tupletParam = [logo.tupletParams[logo.tupletRhythms[i][1]]]; tupletParam.push([]); for ( let j = 2; @@ -1138,10 +1141,10 @@ function setupWidgetBlocks() { logo.inStatusMatrix = true; - let listenerName = "_status_" + turtle; + const listenerName = "_status_" + turtle; logo.setDispatchBlock(blk, turtle, listenerName); - let __listener = function(event) { + const __listener = function(event) { logo.statusMatrix.init(logo); logo.inStatusMatrix = false; }; diff --git a/js/widgets/rhythmruler.js b/js/widgets/rhythmruler.js index b7d0653eac..dc217b5bbf 100644 --- a/js/widgets/rhythmruler.js +++ b/js/widgets/rhythmruler.js @@ -1,3 +1,4 @@ +/* eslint-disable no-undef */ /** * @file This contains the prototype of the rhythmruler Widget * @@ -42,44 +43,44 @@ class RhythmRuler { */ constructor() { // There is one ruler per drum. - this.Drums = []; - // Rulers, one per drum, contain the subdivisions defined by rhythm blocks. - this.Rulers = []; - // Save the history of divisions so as to be able to restore them. - this._dissectHistory = []; - this._undoList = []; - - this._playing = false; - this._playingOne = false; - this._playingAll = false; - this._cellCounter = 0; - - // Keep a elapsed time for each ruler to maintain sync. - this._elapsedTimes = []; - // Starting time from which we measure for sync. - this._startingTime = null; - - this._offsets = []; - this._rulerSelected = 0; - this._rulerPlaying = -1; - - this._tapMode = false; - this._tapTimes = []; - this._tapCell = null; - this._tapEndTime = null; - - this._longPressStartTime = null; - this._inLongPress = false; - - this._mouseDownCell = null; - this._mouseUpCell = null; - - this._wheel = null; - - // Element references - this._dissectNumber = null; - this._progressBar = null; - this._rulers = []; + this.Drums = []; + // Rulers, one per drum, contain the subdivisions defined by rhythm blocks. + this.Rulers = []; + // Save the history of divisions so as to be able to restore them. + this._dissectHistory = []; + this._undoList = []; + + this._playing = false; + this._playingOne = false; + this._playingAll = false; + this._cellCounter = 0; + + // Keep a elapsed time for each ruler to maintain sync. + this._elapsedTimes = []; + // Starting time from which we measure for sync. + this._startingTime = null; + + this._offsets = []; + this._rulerSelected = 0; + this._rulerPlaying = -1; + + this._tapMode = false; + this._tapTimes = []; + this._tapCell = null; + this._tapEndTime = null; + + this._longPressStartTime = null; + this._inLongPress = false; + + this._mouseDownCell = null; + this._mouseUpCell = null; + + this._wheel = null; + + // Element references + this._dissectNumber = null; + this._progressBar = null; + this._rulers = []; } /** @@ -111,11 +112,10 @@ class RhythmRuler { this._offsets.push(0); } - let w = window.innerWidth; this._cellScale = 1.0; - let iconSize = RhythmRuler.ICONSIZE; + const iconSize = RhythmRuler.ICONSIZE; - let widgetWindow = window.widgetWindows.windowFor(this, "rhythm maker"); + const widgetWindow = window.widgetWindows.windowFor(this, "rhythm maker"); this.widgetWindow = widgetWindow; widgetWindow.clear(); widgetWindow.show(); @@ -129,14 +129,14 @@ class RhythmRuler { // docById('contextWheelDiv').style.display = 'none'; // Save the new dissect history. - let dissectHistory = []; - let drums = []; + const dissectHistory = []; + const drums = []; for (let i = 0; i < this.Rulers.length; i++) { if (this.Drums[i] === null) { continue; } - let history = []; + const history = []; for (let j = 0; j < this.Rulers[i][1].length; j++) { history.push(this.Rulers[i][1][j]); } @@ -148,9 +148,9 @@ class RhythmRuler { // Look for any old entries that we may have missed. for (let i = 0; i < this._dissectHistory.length; i++) { - let drum = this._dissectHistory[i][1]; + const drum = this._dissectHistory[i][1]; if (drums.indexOf(drum) === -1) { - let history = JSON.parse( + const history = JSON.parse( JSON.stringify(this._dissectHistory[i][0]) ); dissectHistory.push([history, drum]); @@ -218,7 +218,7 @@ class RhythmRuler { // An input for setting the dissect number this._dissectNumber = widgetWindow.addInputButton("2"); - this._dissectNumber.onfocus = (event) => { + this._dissectNumber.onfocus = () => { // this._piemenuNumber(['2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16'], numberInput.value); }; @@ -231,7 +231,7 @@ class RhythmRuler { } }; - this._dissectNumber.oninput = (event) => { + this._dissectNumber.oninput = () => { // Put a limit on the size (2 <--> 128). this._dissectNumber.onmouseout = () => { this._dissectNumber.value = Math.max( @@ -275,14 +275,14 @@ class RhythmRuler { // We use an outer div to scroll vertically and an inner div to // scroll horizontally. - let rhythmRulerTable = document.createElement("table"); + const rhythmRulerTable = document.createElement("table"); widgetWindow.getWidgetBody().append(rhythmRulerTable); let wMax = 0; // Each row in the ruler table contains a play button in the // first column and a ruler table in the second column. for (let i = 0; i < this.Rulers.length; i++) { - let rhythmRulerTableRow = rhythmRulerTable.insertRow(); + const rhythmRulerTableRow = rhythmRulerTable.insertRow(); if (beginnerMode) { let w = 0; @@ -295,7 +295,7 @@ class RhythmRuler { wMax = w; } } else { - let drumcell = rhythmRulerTableRow.insertCell(); + const drumcell = rhythmRulerTableRow.insertCell(); drumcell.innerHTML = ''; - let rulerCellTable = docById("rulerCellTable" + i); + const rulerCellTable = docById("rulerCellTable" + i); rulerCellTable.style.textAlign = "center"; rulerCellTable.style.border = "0px"; rulerCellTable.style.borderCollapse = "collapse"; rulerCellTable.cellSpacing = "0px"; rulerCellTable.cellPadding = "0px"; - let rulerRow = rulerCellTable.insertRow(); + const rulerRow = rulerCellTable.insertRow(); this._rulers[i] = rulerRow; rulerRow.setAttribute("data-row", i); for (let j = 0; j < this.Rulers[i][0].length; j++) { - let noteValue = this.Rulers[i][0][j]; - let rulerSubCell = rulerRow.insertCell(-1); + const noteValue = this.Rulers[i][0][j]; + const rulerSubCell = rulerRow.insertCell(-1); rulerSubCell.innerHTML = calcNoteValueToDisplay( noteValue, 1, @@ -446,7 +446,7 @@ class RhythmRuler { continue; } - let rhythmRulerTableRow = this._rulers[drum]; + const rhythmRulerTableRow = this._rulers[drum]; for (let j = 0; j < this._dissectHistory[i][0].length; j++) { if (this._dissectHistory[i][0][j] == undefined) { continue; @@ -457,7 +457,7 @@ class RhythmRuler { if (typeof this._dissectHistory[i][0][j] === "number") { cell = rhythmRulerTableRow.cells[ - this._dissectHistory[i][0][j] + this._dissectHistory[i][0][j] ]; this.__toggleRestState(cell, false); } else if ( @@ -469,7 +469,7 @@ class RhythmRuler { // dissect is [cell, num] cell = rhythmRulerTableRow.cells[ - this._dissectHistory[i][0][j][0] + this._dissectHistory[i][0][j][0] ]; if (cell != undefined) { this.__dissectByNumber( @@ -478,15 +478,15 @@ class RhythmRuler { false ); } else { - console.warn( - "Could not find cell to divide. Did the order of the rhythm blocks change?" - ); + // console.warn( + // "Could not find cell to divide. Did the order of the rhythm blocks change?" + // ); } } else { // divide is [cell, [values]] cell = rhythmRulerTableRow.cells[ - this._dissectHistory[i][0][j][0] + this._dissectHistory[i][0][j][0] ]; if (cell != undefined) { this.__divideFromList( @@ -498,7 +498,7 @@ class RhythmRuler { } } else { // tie is [[cell, value], [cell, value]...] - let history = this._dissectHistory[i][0][j]; + const history = this._dissectHistory[i][0][j]; this._mouseDownCell = rhythmRulerTableRow.cells[history[0][0]]; this._mouseUpCell = @@ -533,7 +533,7 @@ class RhythmRuler { * @returns {void} */ _calculateZebraStripes(rulerno) { - let ruler = this._rulers[rulerno]; + const ruler = this._rulers[rulerno]; let evenColor; if (this._rulerSelected % 2 === 0) { evenColor = platformColor.selectorBackground; @@ -581,7 +581,7 @@ class RhythmRuler { } if (this._tapMode && this._tapTimes.length > 0) { - let d = new Date(); + const d = new Date(); this._tapTimes.push(d.getTime()); return; } @@ -597,12 +597,12 @@ class RhythmRuler { } if (this._playing) { - console.warn("You cannot dissect while widget is playing."); + // console.warn("You cannot dissect while widget is playing."); return; } else if (this._tapMode) { // Tap a rhythm by clicking in a cell. if (this._tapCell === null) { - let noteValues = this.Rulers[this._rulerSelected][0]; + const noteValues = this.Rulers[this._rulerSelected][0]; this._tapCell = event.target; if (noteValues[this._tapCell.cellIndex] < 0) { // Don't allow tapping in rests. @@ -632,7 +632,7 @@ class RhythmRuler { if (this.Drums[this._rulerSelected] === null) { drum = "snare drum"; } else { - let drumBlockNo = logo.blocks.blockList[ + const drumBlockNo = logo.blocks.blockList[ this.Drums[this._rulerSelected] ].connections[1]; drum = logo.blocks.blockList[drumBlockNo].value; @@ -649,11 +649,10 @@ class RhythmRuler { } setTimeout(() => { - this.__startTapping(noteValues, interval); + this.__startTapping(interval); }, interval); } } else { - let noteValues = this.Rulers[this._rulerSelected][0]; let inputNum = this._dissectNumber.value; if (inputNum === "" || isNaN(inputNum)) { inputNum = 2; @@ -680,7 +679,7 @@ class RhythmRuler { * @param {number} interval * @returns {void} */ - __startTapping(noteValues, interval, event) { + __startTapping(interval, event) { const d = new Date(); this._tapTimes = [d.getTime()]; this._tapEndTime = this._tapTimes[0] + interval; @@ -733,7 +732,7 @@ class RhythmRuler { typeof this._rulerSelected === "string" || typeof this._rulerSelected === "number" ) { - let noteValues = this.Rulers[this._rulerSelected][0]; + const noteValues = this.Rulers[this._rulerSelected][0]; if (last(this._tapTimes) > this._tapEndTime) { this._tapTimes[this._tapTimes.length - 1] = this._tapEndTime; @@ -845,8 +844,8 @@ class RhythmRuler { } this._rulerSelected = cell.parentNode.getAttribute("data-row"); - let noteValues = this.Rulers[this._rulerSelected][0]; - let noteValue = noteValues[cell.cellIndex]; + const noteValues = this.Rulers[this._rulerSelected][0]; + const noteValue = noteValues[cell.cellIndex]; let obj; if (noteValue < 0) { obj = rationalToFraction( @@ -891,8 +890,7 @@ class RhythmRuler { this._rulerSelected = cell.parentNode.getAttribute( "data-row" ); - let noteValues = this.Rulers[this._rulerSelected][0]; - let noteValue = noteValues[cell.cellIndex]; + // const noteValues = this.Rulers[this._rulerSelected][0]; cell.style.backgroundColor = platformColor.selectorBackground; } @@ -929,7 +927,7 @@ class RhythmRuler { cell.parentNode.getAttribute("data-row") ); } else { - console.error("Rhythm Ruler: null cell found on click"); + // console.error("Rhythm Ruler: null cell found on click"); } } @@ -986,8 +984,8 @@ class RhythmRuler { if (cell !== null && cell.parentNode !== null) { this._rulerSelected = cell.parentNode.getAttribute("data-row"); - let noteValues = this.Rulers[this._rulerSelected][0]; - let noteValue = noteValues[cell.cellIndex]; + const noteValues = this.Rulers[this._rulerSelected][0]; + const noteValue = noteValues[cell.cellIndex]; const __mouseOverHandler = (event) => { const cell = event.target; @@ -998,8 +996,8 @@ class RhythmRuler { let obj; this._rulerSelected = cell.parentNode.getAttribute("data-row"); - let noteValues = this.Rulers[this._rulerSelected][0]; - let noteValue = noteValues[cell.cellIndex]; + const noteValues = this.Rulers[this._rulerSelected][0]; + const noteValue = noteValues[cell.cellIndex]; if (noteValue < 0) { obj = rationalToFraction( Math.abs(Math.abs(-1 / noteValue)) @@ -1081,16 +1079,16 @@ class RhythmRuler { return; } - let ruler = this._rulers[this._rulerSelected]; + const ruler = this._rulers[this._rulerSelected]; const newCellIndex = cell.cellIndex; if ( typeof this._rulerSelected === "string" || typeof this._rulerSelected === "number" ) { - let noteValues = this.Rulers[this._rulerSelected][0]; + const noteValues = this.Rulers[this._rulerSelected][0]; - let divisionHistory = this.Rulers[this._rulerSelected][1]; + const divisionHistory = this.Rulers[this._rulerSelected][1]; if (addToUndoList) { this._undoList.push(["tap", this._rulerSelected]); } @@ -1134,7 +1132,7 @@ class RhythmRuler { * @param {number} inputNum * @param {boolean} addToUndoList * @returns {void} - */ + */ __dissectByNumber(cell, inputNum, addToUndoList) { if (typeof cell !== "object") { @@ -1145,16 +1143,16 @@ class RhythmRuler { return; } - let ruler = this._rulers[this._rulerSelected]; + const ruler = this._rulers[this._rulerSelected]; const newCellIndex = cell.cellIndex; if ( typeof this._rulerSelected === "string" || typeof this._rulerSelected === "number" ) { - let noteValues = this.Rulers[this._rulerSelected][0]; + const noteValues = this.Rulers[this._rulerSelected][0]; - let noteValue = noteValues[newCellIndex]; + const noteValue = noteValues[newCellIndex]; if (inputNum * noteValue > 256) { logo.errorMsg( _("Maximum value of 256 has been exceeded.") @@ -1164,7 +1162,7 @@ class RhythmRuler { logo.hideMsgs(); } - let divisionHistory = this.Rulers[this._rulerSelected][1]; + const divisionHistory = this.Rulers[this._rulerSelected][1]; if (addToUndoList) { this._undoList.push(["dissect", this._rulerSelected]); } @@ -1187,7 +1185,7 @@ class RhythmRuler { noteValues.splice(newCellIndex, 1); for (let i = 0; i < inputNum; i++) { - let newCell = ruler.insertCell(newCellIndex + i); + const newCell = ruler.insertCell(newCellIndex + i); noteValues.splice(newCellIndex + i, 0, newNoteValue); newCell.style.width = newCellWidth + "px"; @@ -1215,11 +1213,11 @@ class RhythmRuler { * @param {Event} event - The triggering event. * @param {string} ruler * @returns {void} - */ + */ _tieRuler(event, ruler) { if (this._playing) { - console.warn("You cannot tie while widget is playing."); + // console.warn("You cannot tie while widget is playing."); return; } else if (this._tapMode) { // If we are tapping, then treat a tie as a tap. @@ -1241,10 +1239,10 @@ class RhythmRuler { * @private * @param {boolean} addToUndoList * @returns {void} - */ + */ __tie(addToUndoList) { - let ruler = this._rulers[this._rulerSelected]; + const ruler = this._rulers[this._rulerSelected]; if (this._mouseDownCell === null || this._mouseUpCell === null) { return; @@ -1278,19 +1276,19 @@ class RhythmRuler { noteValues = this.Rulers[this._rulerSelected][0]; - let divisionHistory = this.Rulers[this._rulerSelected][1]; + const divisionHistory = this.Rulers[this._rulerSelected][1]; if (addToUndoList) { this._undoList.push(["tie", this._rulerSelected]); } - let history = []; + const history = []; for (let i = downCellIndex; i < upCellIndex + 1; i++) { history.push([i, noteValues[i]]); } divisionHistory.push(history); - let oldNoteValue = noteValues[downCellIndex]; + const oldNoteValue = noteValues[downCellIndex]; let noteValue = Math.abs(1 / oldNoteValue); // Delete all the cells between down and up except the down @@ -1328,7 +1326,7 @@ class RhythmRuler { /** * @private * @returns {void} - */ + */ _undo () { // FIXME: Add undo for REST @@ -1344,23 +1342,23 @@ class RhythmRuler { return; } - let obj = this._undoList.pop(); - let lastRuler = obj[1]; - let divisionHistory = this.Rulers[lastRuler][1]; + const obj = this._undoList.pop(); + const lastRuler = obj[1]; + const divisionHistory = this.Rulers[lastRuler][1]; if (divisionHistory.length === 0) { return; } - let ruler = this._rulers[lastRuler]; - let noteValues = this.Rulers[lastRuler][0]; + const ruler = this._rulers[lastRuler]; + const noteValues = this.Rulers[lastRuler][0]; if (obj[0] === "dissect") { - let inputNum = divisionHistory[divisionHistory.length - 1][1]; - let newCellIndex = divisionHistory[divisionHistory.length - 1][0]; - let cellWidth = ruler.cells[newCellIndex].style.width; - let newCellWidth = parseFloat(cellWidth) * inputNum; - let oldCellNoteValue = noteValues[newCellIndex]; - let newNoteValue = oldCellNoteValue / inputNum; + const inputNum = divisionHistory[divisionHistory.length - 1][1]; + const newCellIndex = divisionHistory[divisionHistory.length - 1][0]; + const cellWidth = ruler.cells[newCellIndex].style.width; + const newCellWidth = parseFloat(cellWidth) * inputNum; + const oldCellNoteValue = noteValues[newCellIndex]; + const newNoteValue = oldCellNoteValue / inputNum; const newCell = ruler.insertCell(newCellIndex); newCell.style.width = this._noteWidth(newNoteValue) + "px"; @@ -1385,19 +1383,18 @@ class RhythmRuler { ruler.deleteCell(newCellIndex + 1); } } else if (obj[0] === "tap") { - let newCellIndex = last(divisionHistory)[0]; - let oldNoteValues = last(divisionHistory)[1]; + const newCellIndex = last(divisionHistory)[0]; + const oldNoteValues = last(divisionHistory)[1]; // Calculate the new note value based on the sum of the // oldnoteValues. - let oldCellNoteValue = noteValues[newCellIndex]; let sum = 0; for (let i = 0; i < oldNoteValues.length; i++) { sum += 1 / oldNoteValues[i]; } - let newNoteValue = 1 / sum; - let newCellWidth = this._noteWidth(newNoteValue); + const newNoteValue = 1 / sum; + const newCellWidth = this._noteWidth(newNoteValue); const newCell = ruler.insertCell(newCellIndex); newCell.style.width = newCellWidth + "px"; @@ -1408,7 +1405,7 @@ class RhythmRuler { newCell.style.backgroundColor = platformColor.selectorBackground; - let obj = rationalToFraction(newNoteValue); + const obj = rationalToFraction(newNoteValue); newCell.innerHTML = calcNoteValueToDisplay( obj[1], obj[0], @@ -1424,13 +1421,13 @@ class RhythmRuler { ruler.deleteCell(newCellIndex + 1); } } else if (obj[0] === "tie") { - let history = last(divisionHistory); + const history = last(divisionHistory); // The old cell is the same as the first entry in the // history. Dissect the old cell into history.length // parts and restore their size and note values. if (history.length > 0) { - let oldCell = ruler.cells[history[0][0]]; - let oldCellWidth = this._noteWidth(history[0][1]); + const oldCell = ruler.cells[history[0][0]]; + const oldCellWidth = this._noteWidth(history[0][1]); oldCell.style.width = oldCellWidth + "px"; oldCell.style.minWidth = oldCell.style.width; oldCell.style.height = RhythmRuler.RULERHEIGHT + "px"; @@ -1446,7 +1443,7 @@ class RhythmRuler { for (let i = 1; i < history.length; i++) { const newCell = ruler.insertCell(history[0][0] + i); - let newCellWidth = this._noteWidth(history[i][1]); + const newCellWidth = this._noteWidth(history[i][1]); newCell.style.width = newCellWidth + "px"; newCell.style.minWidth = newCell.style.width; newCell.style.height = RhythmRuler.RULERHEIGHT + "px"; @@ -1469,10 +1466,10 @@ class RhythmRuler { this.Rulers[lastRuler][0] = noteValues; } else { - console.warn("empty history encountered... skipping undo"); + // console.warn("empty history encountered... skipping undo"); } } else if (obj[0] === "rest") { - let newCellIndex = last(divisionHistory); + const newCellIndex = last(divisionHistory); const cell = ruler.cells[newCellIndex]; this.__toggleRestState(cell, false); divisionHistory.pop(); @@ -1488,11 +1485,11 @@ class RhythmRuler { /** * @private * @returns {void} - */ + */ _tap() { this._tapMode = true; - let iconSize = RhythmRuler.ICONSIZE; + const iconSize = RhythmRuler.ICONSIZE; this._tapButton.innerHTML = ' { - let ruler = this._rulers[selectedRuler]; - let noteValues = this.Rulers[selectedRuler][0]; + const ruler = this._rulers[selectedRuler]; + const noteValues = this.Rulers[selectedRuler][0]; // Get the first word of drum's name (ignore the word 'drum' itself) // and add 'rhythm'. let stack_value; @@ -1792,8 +1789,8 @@ class RhythmRuler { " " + _("rhythm"); } - let delta = selectedRuler * 42; - let newStack = [ + const delta = selectedRuler * 42; + const newStack = [ [ 0, ["action", { collapsed: true }], @@ -1813,10 +1810,10 @@ class RhythmRuler { sameNoteValue += 1; continue; } else { - let idx = newStack.length; - let noteValue = noteValues[i]; + const idx = newStack.length; + const noteValue = noteValues[i]; - let obj = rationalToFraction(1 / Math.abs(noteValue)); + const obj = rationalToFraction(1 / Math.abs(noteValue)); newStack.push([ idx, @@ -1890,20 +1887,20 @@ class RhythmRuler { * @private * @param {number} selectedRuler * @returns {void} - */ + */ _saveTuplets(selectedRuler) { - for (let name in logo.blocks.palettes.dict) { + for (const name in logo.blocks.palettes.dict) { logo.blocks.palettes.dict[name].hideMenu(true); } logo.refreshCanvas(); setTimeout(() => { - let ruler = this._rulers[selectedRuler]; - let noteValues = this.Rulers[selectedRuler][0]; + const ruler = this._rulers[selectedRuler]; + const noteValues = this.Rulers[selectedRuler][0]; let stack_value; if (this.Drums[selectedRuler] === null) { stack_value = _("rhythm"); @@ -1916,8 +1913,8 @@ class RhythmRuler { " " + _("rhythm"); } - let delta = selectedRuler * 42; - let newStack = [ + const delta = selectedRuler * 42; + const newStack = [ [ 0, ["action", { collapsed: true }], @@ -1937,10 +1934,10 @@ class RhythmRuler { sameNoteValue += 1; continue; } else { - let idx = newStack.length; - let noteValue = noteValues[i]; - let obj = rationalToFraction(1 / Math.abs(noteValue)); - let n = obj[1] / sameNoteValue; + const idx = newStack.length; + const noteValue = noteValues[i]; + const obj = rationalToFraction(1 / Math.abs(noteValue)); + const n = obj[1] / sameNoteValue; if (Number.isInteger(n)) { newStack.push([ idx, @@ -2065,19 +2062,19 @@ class RhythmRuler { * @private * @param {number} selectedRuler * @returns {void} - */ + */ _saveTupletsMerged(noteValues) { - for (let name in logo.blocks.palettes.dict) { + for (const name in logo.blocks.palettes.dict) { logo.blocks.palettes.dict[name].hideMenu(true); } logo.refreshCanvas(); - let stack_value = _("rhythm"); - let delta = 42; - let newStack = [ + const stack_value = _("rhythm"); + const delta = 42; + const newStack = [ [ 0, ["action", { collapsed: true }], @@ -2097,9 +2094,9 @@ class RhythmRuler { sameNoteValue += 1; continue; } else { - let idx = newStack.length; - let noteValue = noteValues[i]; - let obj = rationalToFraction(1 / Math.abs(noteValue)); + const idx = newStack.length; + const noteValue = noteValues[i]; + const obj = rationalToFraction(1 / Math.abs(noteValue)); newStack.push([ idx, "rhythm2", @@ -2162,7 +2159,7 @@ class RhythmRuler { * @private * @param {number} selectedRuler * @returns {void} - */ + */ _saveMachine (selectedRuler) { // We are either saving a drum machine or a voice machine. @@ -2170,7 +2167,7 @@ class RhythmRuler { if (this.Drums[selectedRuler] === null) { drum = "snare drum"; } else { - let drumBlockNo = logo.blocks.blockList[ + const drumBlockNo = logo.blocks.blockList[ this.Drums[selectedRuler] ].connections[1]; drum = logo.blocks.blockList[drumBlockNo].value; @@ -2202,20 +2199,20 @@ class RhythmRuler { * @param {string} drum * @param {boolean} effect * @returns {void} - */ + */ _saveDrumMachine(selectedRuler, drum, effect) { - for (let name in logo.blocks.palettes.dict) { + for (const name in logo.blocks.palettes.dict) { logo.blocks.palettes.dict[name].hideMenu(true); } logo.refreshCanvas(); setTimeout(() => { - let ruler = this._rulers[selectedRuler]; - let noteValues = this.Rulers[selectedRuler][0]; - let delta = selectedRuler * 42; + const ruler = this._rulers[selectedRuler]; + const noteValues = this.Rulers[selectedRuler][0]; + const delta = selectedRuler * 42; // Just save the action, not the drum machine itself. // let newStack = [[0, ['start', {'collapsed': false}], 100 + delta, 100 + delta, [null, 1, null]]]; @@ -2233,7 +2230,7 @@ class RhythmRuler { _("action"); } - let newStack = [ + const newStack = [ [ 0, ["action", { collapsed: true }], @@ -2253,10 +2250,10 @@ class RhythmRuler { sameNoteValue += 1; continue; } else { - let idx = newStack.length; - let noteValue = noteValues[i]; + const idx = newStack.length; + const noteValue = noteValues[i]; - let obj = rationalToFraction(1 / Math.abs(noteValue)); + const obj = rationalToFraction(1 / Math.abs(noteValue)); if (sameNoteValue === 1) { // Add a note block. @@ -2513,7 +2510,7 @@ class RhythmRuler { * @param {number} selectedRuler * @param {string} voice * @returns {void} - */ + */ _saveVoiceMachine(selectedRuler, voice) { @@ -2524,9 +2521,9 @@ class RhythmRuler { logo.refreshCanvas(); setTimeout(() => { - let ruler = this._rulers[selectedRuler]; - let noteValues = this.Rulers[selectedRuler][0]; - let delta = selectedRuler * 42; + const ruler = this._rulers[selectedRuler]; + const noteValues = this.Rulers[selectedRuler][0]; + const delta = selectedRuler * 42; // Just save the action, not the drum machine itself. // let newStack = [[0, ['start', {'collapsed': false}], 100 + delta, 100 + delta, [null, 1, null]]]; @@ -2550,7 +2547,7 @@ class RhythmRuler { _("action"); } - let newStack = [ + const newStack = [ [ 0, ["action", { collapsed: true }], @@ -2573,10 +2570,10 @@ class RhythmRuler { sameNoteValue += 1; continue; } else { - let idx = newStack.length; - let noteValue = noteValues[i]; + const idx = newStack.length; + const noteValue = noteValues[i]; - let obj = rationalToFraction(1 / Math.abs(noteValue)); + const obj = rationalToFraction(1 / Math.abs(noteValue)); if (sameNoteValue === 1) { // Add a note block. @@ -2891,7 +2888,7 @@ class RhythmRuler { /** * @private * @returns {array} - */ + */ _mergeRulers() { // Merge the rulers into one set of rhythms. @@ -2899,7 +2896,7 @@ class RhythmRuler { let noteValues; for (let r = 0; r < this.Rulers.length; r++) { let t = 0; - let selectedRuler = this.Rulers[r]; + const selectedRuler = this.Rulers[r]; noteValues = selectedRuler[0]; for (let i = 0; i < noteValues.length; i++) { t += 1 / noteValues[i]; @@ -2928,7 +2925,7 @@ class RhythmRuler { /** * @private * @returns {boolean} - */ + */ _get_save_lock() { return this._save_lock; @@ -2937,13 +2934,13 @@ class RhythmRuler { /** * @public * @returns {void} - */ + */ saveDissectHistory() { // Save the new dissect history. - let dissectHistory = []; - let drums = []; + const dissectHistory = []; + const drums = []; let drum; let history; for (let i = 0; i < this.Rulers.length; i++) { @@ -2975,7 +2972,7 @@ class RhythmRuler { this._dissectHistory = JSON.parse(JSON.stringify(dissectHistory)); } - _piemenuRuler(selectedRuler) { + _piemenuRuler() { return; // In progress /* // piemenu version of ruler @@ -3024,96 +3021,6 @@ class RhythmRuler { */ } - /** - * @private - * @returns {void} - */ - - _piemenuNumber(wheelValues, selectedValue) { - // input form and wheelNav pie menu for number selection - docById("wheelDiv").style.display = ""; - - // the number selector - this._numberWheel = new wheelnav("wheelDiv", null, 600, 600); - // exit button - this._exitWheel = new wheelnav("_exitWheel", this._numberWheel.raphael); - - let wheelLabels = []; - for (let i = 0; i < wheelValues.length; i++) { - wheelLabels.push(wheelValues[i].toString()); - } - - // spacer - wheelLabels.push(null); - - wheelnav.cssMode = true; - - this._numberWheel.keynavigateEnabled = true; - - this._numberWheel.colors = ["#ffb2bc", "#ffccd6"]; - this._numberWheel.slicePathFunction = slicePath().DonutSlice; - this._numberWheel.slicePathCustom = slicePath().DonutSliceCustomization(); - this._numberWheel.slicePathCustom.minRadiusPercent = 0.2; - if (wheelValues.length > 16) { - this._numberWheel.slicePathCustom.maxRadiusPercent = 1.0; - } else { - this._numberWheel.slicePathCustom.maxRadiusPercent = 0.6; - } - - this._numberWheel.sliceSelectedPathCustom = this._numberWheel.slicePathCustom; - this._numberWheel.sliceInitPathCustom = this._numberWheel.slicePathCustom; - // this._numberWheel.titleRotateAngle = 0; - this._numberWheel.animatetime = 300; - this._numberWheel.createWheel(wheelLabels); - - this._exitWheel.colors = ["#808080", "#c0c0c0"]; - this._exitWheel.slicePathFunction = slicePath().DonutSlice; - this._exitWheel.slicePathCustom = slicePath().DonutSliceCustomization(); - this._exitWheel.slicePathCustom.minRadiusPercent = 0.0; - this._exitWheel.slicePathCustom.maxRadiusPercent = 0.2; - this._exitWheel.sliceSelectedPathCustom = this._exitWheel.slicePathCustom; - this._exitWheel.sliceInitPathCustom = this._exitWheel.slicePathCustom; - this._exitWheel.clickModeRotate = false; - this._exitWheel.createWheel(["x", " "]); - - - - const __selectionChanged = () => { - this._dissectNumber.value = - wheelValues[this._numberWheel.selectedNavItemIndex]; - }; - - const __exitMenu = () => { - let d = new Date(); - this._piemenuExitTime = d.getTime(); - docById("wheelDiv").style.display = "none"; - this._numberWheel.removeWheel(); - this._exitWheel.removeWheel(); - }; - - this._positionWheel(); - - // Navigate to a the current number value. - let i = wheelValues.indexOf(selectedValue); - if (i === -1) { - i = 0; - } - - this._numberWheel.navigateWheel(i); - - // Hide the widget when the selection is made. - for (let i = 0; i < wheelLabels.length; i++) { - this._numberWheel.navItems[i].navigateFunction = () => { - __selectionChanged(); - __exitMenu(); - }; - } - - // Or use the exit wheel... - this._exitWheel.navItems[0].navigateFunction = () => { - __exitMenu(); - }; - } /** * @private @@ -3131,9 +3038,9 @@ class RhythmRuler { docById("wheelDiv").style.width = "300px"; // Position the widget over the note block. - let x = this._left + 100; - let y = this._top; - let selectorWidth = 150; + const x = this._left + 100; + const y = this._top; + const selectorWidth = 150; docById("wheelDiv").style.left = Math.min( From ac82c1ed36e012a4e48042408ce404defb1bdfb8 Mon Sep 17 00:00:00 2001 From: Anindya Kundu Date: Sun, 31 Jan 2021 19:07:08 +0530 Subject: [PATCH 6/7] Fix regression --- js/widgets/rhythmruler.js | 1006 +++++++------------------------------ 1 file changed, 182 insertions(+), 824 deletions(-) diff --git a/js/widgets/rhythmruler.js b/js/widgets/rhythmruler.js index dc217b5bbf..880b56cde4 100644 --- a/js/widgets/rhythmruler.js +++ b/js/widgets/rhythmruler.js @@ -1,4 +1,3 @@ -/* eslint-disable no-undef */ /** * @file This contains the prototype of the rhythmruler Widget * @@ -18,7 +17,7 @@ /* global TONEBPM, Singer, logo, _, delayExecution, docById, calcNoteValueToDisplay, platformColor, beginnerMode, last, EIGHTHNOTEWIDTH, nearestBeat, rationalToFraction, DRUMNAMES, VOICENAMES, - EFFECTSNAMES, wheelnav, slicePath + EFFECTSNAMES */ /* exported RhythmRuler */ @@ -42,7 +41,7 @@ class RhythmRuler { * @constructor */ constructor() { - // There is one ruler per drum. + // There is one ruler per drum. this.Drums = []; // Rulers, one per drum, contain the subdivisions defined by rhythm blocks. this.Rulers = []; @@ -122,7 +121,6 @@ class RhythmRuler { // For the button callbacks - widgetWindow.onclose = () => { // If the piemenu was open, close it. // docById('wheelDiv').style.display = 'none'; @@ -150,9 +148,7 @@ class RhythmRuler { for (let i = 0; i < this._dissectHistory.length; i++) { const drum = this._dissectHistory[i][1]; if (drums.indexOf(drum) === -1) { - const history = JSON.parse( - JSON.stringify(this._dissectHistory[i][0]) - ); + const history = JSON.parse(JSON.stringify(this._dissectHistory[i][0])); dissectHistory.push([history, drum]); } } @@ -167,11 +163,7 @@ class RhythmRuler { this.widgetWindow.destroy(); }; - this._playAllCell = widgetWindow.addButton( - "play-button.svg", - iconSize, - _("Play all") - ); + this._playAllCell = widgetWindow.addButton("play-button.svg", iconSize, _("Play all")); this._playAllCell.onclick = () => { if (this._playing) { this.__pause(); @@ -234,42 +226,24 @@ class RhythmRuler { this._dissectNumber.oninput = () => { // Put a limit on the size (2 <--> 128). this._dissectNumber.onmouseout = () => { - this._dissectNumber.value = Math.max( - this._dissectNumber.value, - 2 - ); + this._dissectNumber.value = Math.max(this._dissectNumber.value, 2); }; - this._dissectNumber.value = Math.max( - Math.min(this._dissectNumber.value, 128), - 2 - ); + this._dissectNumber.value = Math.max(Math.min(this._dissectNumber.value, 128), 2); }; - widgetWindow.addButton( - "restore-button.svg", - iconSize, - _("Undo") - ).onclick = () => { + widgetWindow.addButton("restore-button.svg", iconSize, _("Undo")).onclick = () => { this._undo(); }; //.TRANS: user can tap out a rhythm by clicking on a ruler. - this._tapButton = widgetWindow.addButton( - "tap-button.svg", - iconSize, - _("Tap a rhythm") - ); + this._tapButton = widgetWindow.addButton("tap-button.svg", iconSize, _("Tap a rhythm")); this._tapButton.onclick = () => { this._tap(); }; //.TRANS: clear all subdivisions from the ruler. - widgetWindow.addButton( - "erase-button.svg", - iconSize, - _("Clear") - ).onclick = () => { + widgetWindow.addButton("erase-button.svg", iconSize, _("Clear")).onclick = () => { this._clear(); }; @@ -329,10 +303,7 @@ class RhythmRuler { this._startingTime = null; this._elapsedTimes[id] = 0; this._offsets[id] = 0; - setTimeout( - this._calculateZebraStripes(id), - 1000 - ); + setTimeout(this._calculateZebraStripes(id), 1000); } } else { if (this._playingOne === false) { @@ -365,8 +336,7 @@ class RhythmRuler { const rulerCell = rhythmRulerTableRow.insertCell(); // Create individual rulers as tables. - rulerCell.innerHTML = - '
'; + rulerCell.innerHTML = '
'; const rulerCellTable = docById("rulerCellTable" + i); rulerCellTable.style.textAlign = "center"; @@ -381,11 +351,7 @@ class RhythmRuler { for (let j = 0; j < this.Rulers[i][0].length; j++) { const noteValue = this.Rulers[i][0][j]; const rulerSubCell = rulerRow.insertCell(-1); - rulerSubCell.innerHTML = calcNoteValueToDisplay( - noteValue, - 1, - this._cellScale - ); + rulerSubCell.innerHTML = calcNoteValueToDisplay(noteValue, 1, this._cellScale); rulerSubCell.style.height = RhythmRuler.RULERHEIGHT + "px"; rulerSubCell.style.minHeight = rulerSubCell.style.height; rulerSubCell.style.maxHeight = rulerSubCell.style.height; @@ -398,46 +364,35 @@ class RhythmRuler { rulerSubCell.style.lineHeight = 60 + " % "; if (i % 2 === 0) { if (j % 2 === 0) { - rulerSubCell.style.backgroundColor = - platformColor.selectorBackground; + rulerSubCell.style.backgroundColor = platformColor.selectorBackground; } else { - rulerSubCell.style.backgroundColor = - platformColor.selectorSelected; + rulerSubCell.style.backgroundColor = platformColor.selectorSelected; } } else { if (j % 2 === 0) { - rulerSubCell.style.backgroundColor = - platformColor.selectorSelected; + rulerSubCell.style.backgroundColor = platformColor.selectorSelected; } else { - rulerSubCell.style.backgroundColor = - platformColor.selectorBackground; + rulerSubCell.style.backgroundColor = platformColor.selectorBackground; } } - this.__addCellEventHandlers( - rulerSubCell, - this._noteWidth(noteValue), - noteValue - ); + this.__addCellEventHandlers(rulerSubCell, this._noteWidth(noteValue), noteValue); } // Match the play button height to the ruler height. rhythmRulerTableRow.cells[0].style.width = RhythmRuler.BUTTONSIZE + "px"; rhythmRulerTableRow.cells[0].style.minWidth = RhythmRuler.BUTTONSIZE + "px"; rhythmRulerTableRow.cells[0].style.maxWidth = RhythmRuler.BUTTONSIZE + "px"; - rhythmRulerTableRow.cells[0].style.height = - rulerRow.offsetHeight + "px"; - rhythmRulerTableRow.cells[0].style.minHeight = - rulerRow.offsetHeight + "px"; - rhythmRulerTableRow.cells[0].style.maxHeight = - rulerRow.offsetHeight + "px"; + rhythmRulerTableRow.cells[0].style.height = rulerRow.offsetHeight + "px"; + rhythmRulerTableRow.cells[0].style.minHeight = rulerRow.offsetHeight + "px"; + rhythmRulerTableRow.cells[0].style.maxHeight = rulerRow.offsetHeight + "px"; rhythmRulerTableRow.cells[0].style.verticalAlign = "middle"; } // Restore dissect history. let cell; for (let drum = 0; drum < this.Drums.length; drum++) { - if (this.Drums[i] === null) { + if (drum === null) { continue; } @@ -455,22 +410,12 @@ class RhythmRuler { this._rulerSelected = drum; if (typeof this._dissectHistory[i][0][j] === "number") { - cell = - rhythmRulerTableRow.cells[ - this._dissectHistory[i][0][j] - ]; + cell = rhythmRulerTableRow.cells[this._dissectHistory[i][0][j]]; this.__toggleRestState(cell, false); - } else if ( - typeof this._dissectHistory[i][0][j][0] === "number" - ) { - if ( - typeof this._dissectHistory[i][0][j][1] === "number" - ) { + } else if (typeof this._dissectHistory[i][0][j][0] === "number") { + if (typeof this._dissectHistory[i][0][j][1] === "number") { // dissect is [cell, num] - cell = - rhythmRulerTableRow.cells[ - this._dissectHistory[i][0][j][0] - ]; + cell = rhythmRulerTableRow.cells[this._dissectHistory[i][0][j][0]]; if (cell != undefined) { this.__dissectByNumber( cell, @@ -484,10 +429,7 @@ class RhythmRuler { } } else { // divide is [cell, [values]] - cell = - rhythmRulerTableRow.cells[ - this._dissectHistory[i][0][j][0] - ]; + cell = rhythmRulerTableRow.cells[this._dissectHistory[i][0][j][0]]; if (cell != undefined) { this.__divideFromList( cell, @@ -499,10 +441,8 @@ class RhythmRuler { } else { // tie is [[cell, value], [cell, value]...] const history = this._dissectHistory[i][0][j]; - this._mouseDownCell = - rhythmRulerTableRow.cells[history[0][0]]; - this._mouseUpCell = - rhythmRulerTableRow.cells[last(history)[0]]; + this._mouseDownCell = rhythmRulerTableRow.cells[history[0][0]]; + this._mouseUpCell = rhythmRulerTableRow.cells[last(history)[0]]; if (this._mouseUpCell != undefined) { this.__tie(false); } @@ -547,25 +487,21 @@ class RhythmRuler { newCell.style.borderRadius = "10px"; if (evenColor === platformColor.selectorBackground) { if (i % 2 === 0) { - newCell.style.backgroundColor = - platformColor.selectorBackground; + newCell.style.backgroundColor = platformColor.selectorBackground; } else { - newCell.style.backgroundColor = - platformColor.selectorSelected; + newCell.style.backgroundColor = platformColor.selectorSelected; } } if (evenColor === platformColor.selectorSelected) { if (i % 2 === 0) { - newCell.style.backgroundColor = - platformColor.selectorSelected; + newCell.style.backgroundColor = platformColor.selectorSelected; } else { - newCell.style.backgroundColor = - platformColor.selectorBackground; + newCell.style.backgroundColor = platformColor.selectorBackground; } } } - }; + } /** * @private @@ -573,7 +509,6 @@ class RhythmRuler { * @param {Event} event - The triggering event. * @returns {void} */ - _dissectRuler(event, ruler) { const cell = event.target; if (cell === null) { @@ -616,9 +551,9 @@ class RhythmRuler { '" alt="' + _("tap a rhythm") + '" height="' + - RhythmRuler.ICONSIZE + + RhythmRuler.ICONSIZE + '" width="' + - RhythmRuler.ICONSIZE + + RhythmRuler.ICONSIZE + '" vertical-align="middle">'; return; } @@ -632,24 +567,20 @@ class RhythmRuler { if (this.Drums[this._rulerSelected] === null) { drum = "snare drum"; } else { - const drumBlockNo = logo.blocks.blockList[ - this.Drums[this._rulerSelected] - ].connections[1]; + const drumBlockNo = + logo.blocks.blockList[this.Drums[this._rulerSelected]].connections[1]; drum = logo.blocks.blockList[drumBlockNo].value; } - // FIXME: Should be based on meter for (let i = 0; i < 4; i++) { setTimeout(() => { - logo.synth.trigger( - 0, "C4", Singer.defaultBPMFactor / 16, drum, null, null - ); + logo.synth.trigger(0, "C4", Singer.defaultBPMFactor / 16, drum, null, null); }, (interval * i) / 4); } setTimeout(() => { - this.__startTapping(interval); + this.__startTapping(interval, event); }, interval); } } else { @@ -708,6 +639,7 @@ class RhythmRuler { // Progress once per 8th note. __move(interval / 8, 100 / 8); } + /** * @private * @param {Event} event - The triggering event. @@ -728,10 +660,7 @@ class RhythmRuler { this._tapTimes.push(d.getTime()); this._tapMode = false; - if ( - typeof this._rulerSelected === "string" || - typeof this._rulerSelected === "number" - ) { + if (typeof this._rulerSelected === "string" || typeof this._rulerSelected === "number") { const noteValues = this.Rulers[this._rulerSelected][0]; if (last(this._tapTimes) > this._tapEndTime) { @@ -783,10 +712,7 @@ class RhythmRuler { for (let i = 1; i < this._tapTimes.length; i++) { const dtime = this._tapTimes[i] - this._tapTimes[i - 1]; if (i < this._tapTimes.length - 1) { - obj = nearestBeat( - (100 * dtime) / this._bpmFactor, - minimumBeat - ); + obj = nearestBeat((100 * dtime) / this._bpmFactor, minimumBeat); if (obj[0] === 0) { obj[0] = 1; obj[1] = obj[1] / 2; @@ -800,9 +726,7 @@ class RhythmRuler { // Since the fractional value is noisy, // ensure that the final beat make the // total add up to the proper note value. - obj = rationalToFraction( - 1 / noteValues[this._tapCell.cellIndex] - sum - ); + obj = rationalToFraction(1 / noteValues[this._tapCell.cellIndex] - sum); newNoteValues.push(obj[1] / obj[0]); } } @@ -820,9 +744,9 @@ class RhythmRuler { '" alt="' + _("tap a rhythm") + '" height="' + - RhythmRuler.ICONSIZE + + RhythmRuler.ICONSIZE + '" width="' + - RhythmRuler.ICONSIZE + + RhythmRuler.ICONSIZE + '" vertical-align="middle">'; } @@ -833,10 +757,7 @@ class RhythmRuler { * @param {number} noteValue * @returns {void} */ - __addCellEventHandlers(cell, cellWidth, noteValue) { - - const __mouseOverHandler = (event) => { const cell = event.target; if (cell === null || cell.parentNode === null) { @@ -848,20 +769,12 @@ class RhythmRuler { const noteValue = noteValues[cell.cellIndex]; let obj; if (noteValue < 0) { - obj = rationalToFraction( - Math.abs(Math.abs(-1 / noteValue)) - ); + obj = rationalToFraction(Math.abs(Math.abs(-1 / noteValue))); cell.innerHTML = - calcNoteValueToDisplay(obj[1], obj[0], this._cellScale) + - " " + - _("silence"); + calcNoteValueToDisplay(obj[1], obj[0], this._cellScale) + " " + _("silence"); } else { obj = rationalToFraction(Math.abs(Math.abs(1 / noteValue))); - cell.innerHTML = calcNoteValueToDisplay( - obj[1], - obj[0], - this._cellScale - ); + cell.innerHTML = calcNoteValueToDisplay(obj[1], obj[0], this._cellScale); } }; @@ -887,12 +800,9 @@ class RhythmRuler { const cell = this._mouseDownCell; if (cell !== null && cell.parentNode !== null) { - this._rulerSelected = cell.parentNode.getAttribute( - "data-row" - ); + this._rulerSelected = cell.parentNode.getAttribute("data-row"); // const noteValues = this.Rulers[this._rulerSelected][0]; - cell.style.backgroundColor = - platformColor.selectorBackground; + cell.style.backgroundColor = platformColor.selectorBackground; } }, 1500); }; @@ -922,10 +832,7 @@ class RhythmRuler { if (!this.__getLongPressStatus()) { const cell = event.target; if (cell !== null && cell.parentNode !== null) { - this._dissectRuler( - event, - cell.parentNode.getAttribute("data-row") - ); + this._dissectRuler(event, cell.parentNode.getAttribute("data-row")); } else { // console.error("Rhythm Ruler: null cell found on click"); } @@ -937,11 +844,7 @@ class RhythmRuler { let obj; if (cellWidth > 12 && noteValue > 0) { obj = rationalToFraction(Math.abs(1 / noteValue)); - cell.innerHTML = calcNoteValueToDisplay( - obj[1], - obj[0], - this._cellScale - ); + cell.innerHTML = calcNoteValueToDisplay(obj[1], obj[0], this._cellScale); } else { cell.innerHTML = ""; @@ -966,11 +869,9 @@ class RhythmRuler { * @private * @returns {void} */ - - - __getLongPressStatus () { + __getLongPressStatus() { return this._inLongPress; - }; + } /** * @private @@ -978,10 +879,7 @@ class RhythmRuler { * @param {boolean} addToUndoList * @returns {void} */ - __toggleRestState(cell, addToUndoList) { - - if (cell !== null && cell.parentNode !== null) { this._rulerSelected = cell.parentNode.getAttribute("data-row"); const noteValues = this.Rulers[this._rulerSelected][0]; @@ -999,26 +897,14 @@ class RhythmRuler { const noteValues = this.Rulers[this._rulerSelected][0]; const noteValue = noteValues[cell.cellIndex]; if (noteValue < 0) { - obj = rationalToFraction( - Math.abs(Math.abs(-1 / noteValue)) - ); + obj = rationalToFraction(Math.abs(Math.abs(-1 / noteValue))); cell.innerHTML = - calcNoteValueToDisplay( - obj[1], - obj[0], - this._cellScale - ) + + calcNoteValueToDisplay(obj[1], obj[0], this._cellScale) + " " + _("silence"); } else { - obj = rationalToFraction( - Math.abs(Math.abs(1 / noteValue)) - ); - cell.innerHTML = calcNoteValueToDisplay( - obj[1], - obj[0], - this._cellScale - ); + obj = rationalToFraction(Math.abs(Math.abs(1 / noteValue))); + cell.innerHTML = calcNoteValueToDisplay(obj[1], obj[0], this._cellScale); } }; @@ -1030,11 +916,7 @@ class RhythmRuler { let obj; if (noteValue < 0) { obj = rationalToFraction(Math.abs(1 / noteValue)); - cell.innerHTML = calcNoteValueToDisplay( - obj[1], - obj[0], - this._cellScale - ); + cell.innerHTML = calcNoteValueToDisplay(obj[1], obj[0], this._cellScale); cell.removeEventListener("mouseover", __mouseOverHandler); cell.removeEventListener("mouseout", __mouseOutHandler); } else { @@ -1051,7 +933,7 @@ class RhythmRuler { this._calculateZebraStripes(this._rulerSelected); - divisionHistory = this.Rulers[this._rulerSelected][1]; + const divisionHistory = this.Rulers[this._rulerSelected][1]; if (addToUndoList) { this._undoList.push(["rest", this._rulerSelected]); } @@ -1069,7 +951,6 @@ class RhythmRuler { * @param {boolean} addToUndoList * @returns {void} */ - __divideFromList(cell, newNoteValues, addToUndoList) { if (typeof cell !== "object") { return; @@ -1082,10 +963,7 @@ class RhythmRuler { const ruler = this._rulers[this._rulerSelected]; const newCellIndex = cell.cellIndex; - if ( - typeof this._rulerSelected === "string" || - typeof this._rulerSelected === "number" - ) { + if (typeof this._rulerSelected === "string" || typeof this._rulerSelected === "number") { const noteValues = this.Rulers[this._rulerSelected][0]; const divisionHistory = this.Rulers[this._rulerSelected][1]; @@ -1113,11 +991,7 @@ class RhythmRuler { newCell.style.minHeight = newCell.style.height; newCell.style.maxHeight = newCell.style.height; - this.__addCellEventHandlers( - newCell, - newCellWidth, - newNoteValue - ); + this.__addCellEventHandlers(newCell, newCellWidth, newNoteValue); } this._calculateZebraStripes(this._rulerSelected); @@ -1133,7 +1007,6 @@ class RhythmRuler { * @param {boolean} addToUndoList * @returns {void} */ - __dissectByNumber(cell, inputNum, addToUndoList) { if (typeof cell !== "object") { return; @@ -1146,17 +1019,12 @@ class RhythmRuler { const ruler = this._rulers[this._rulerSelected]; const newCellIndex = cell.cellIndex; - if ( - typeof this._rulerSelected === "string" || - typeof this._rulerSelected === "number" - ) { + if (typeof this._rulerSelected === "string" || typeof this._rulerSelected === "number") { const noteValues = this.Rulers[this._rulerSelected][0]; const noteValue = noteValues[newCellIndex]; if (inputNum * noteValue > 256) { - logo.errorMsg( - _("Maximum value of 256 has been exceeded.") - ); + logo.errorMsg(_("Maximum value of 256 has been exceeded.")); return; } else { logo.hideMsgs(); @@ -1180,8 +1048,7 @@ class RhythmRuler { parseFloat(this._noteWidth(noteValue)) - parseFloat(inputNum) * parseFloat(tempwidth); const newCellWidth = - parseFloat(this._noteWidth(newNoteValue)) + - parseFloat(difference) / inputNum; + parseFloat(this._noteWidth(newNoteValue)) + parseFloat(difference) / inputNum; noteValues.splice(newCellIndex, 1); for (let i = 0; i < inputNum; i++) { @@ -1194,11 +1061,7 @@ class RhythmRuler { newCell.style.minHeight = newCell.style.height; newCell.style.maxHeight = newCell.style.height; - this.__addCellEventHandlers( - newCell, - newCellWidth, - newNoteValue - ); + this.__addCellEventHandlers(newCell, newCellWidth, newNoteValue); } this._calculateZebraStripes(this._rulerSelected); @@ -1207,14 +1070,12 @@ class RhythmRuler { // this._piemenuRuler(this._rulerSelected); } - /** * @private * @param {Event} event - The triggering event. * @param {string} ruler * @returns {void} */ - _tieRuler(event, ruler) { if (this._playing) { // console.warn("You cannot tie while widget is playing."); @@ -1240,7 +1101,6 @@ class RhythmRuler { * @param {boolean} addToUndoList * @returns {void} */ - __tie(addToUndoList) { const ruler = this._rulers[this._rulerSelected]; @@ -1252,10 +1112,7 @@ class RhythmRuler { return; } - if ( - typeof this._rulerSelected === "string" || - typeof this._rulerSelected === "number" - ) { + if (typeof this._rulerSelected === "string" || typeof this._rulerSelected === "number") { let noteValues = this.Rulers[this._rulerSelected][0]; let downCellIndex = this._mouseDownCell.cellIndex; @@ -1327,8 +1184,7 @@ class RhythmRuler { * @private * @returns {void} */ - - _undo () { + _undo() { // FIXME: Add undo for REST logo.synth.stop(); this._startingTime = null; @@ -1406,11 +1262,7 @@ class RhythmRuler { newCell.style.backgroundColor = platformColor.selectorBackground; const obj = rationalToFraction(newNoteValue); - newCell.innerHTML = calcNoteValueToDisplay( - obj[1], - obj[0], - this._cellScale - ); + newCell.innerHTML = calcNoteValueToDisplay(obj[1], obj[0], this._cellScale); noteValues[newCellIndex] = newNoteValue; noteValues.splice(newCellIndex + 1, oldNoteValues.length - 1); @@ -1435,11 +1287,7 @@ class RhythmRuler { oldCell.style.maxHeight = oldCell.style.height; noteValues[history[0][0]] = history[0][1]; - this.__addCellEventHandlers( - oldCell, - oldCellWidth, - history[0][1] - ); + this.__addCellEventHandlers(oldCell, oldCellWidth, history[0][1]); for (let i = 1; i < history.length; i++) { const newCell = ruler.insertCell(history[0][0] + i); @@ -1451,17 +1299,9 @@ class RhythmRuler { newCell.style.maxHeight = newCell.style.height; noteValues.splice(history[0][0] + i, 0, history[i][1]); - newCell.innerHTML = calcNoteValueToDisplay( - history[i][1], - 1, - this._cellScale - ); + newCell.innerHTML = calcNoteValueToDisplay(history[i][1], 1, this._cellScale); - this.__addCellEventHandlers( - newCell, - newCellWidth, - history[i][1] - ); + this.__addCellEventHandlers(newCell, newCellWidth, history[i][1]); } this.Rulers[lastRuler][0] = noteValues; @@ -1481,12 +1321,10 @@ class RhythmRuler { // this._piemenuRuler(this._rulerSelected); } - /** * @private * @returns {void} */ - _tap() { this._tapMode = true; const iconSize = RhythmRuler.ICONSIZE; @@ -1506,7 +1344,6 @@ class RhythmRuler { * @private * @returns {void} */ - _clear() { logo.synth.stop(); logo.resetSynth(0); @@ -1521,9 +1358,9 @@ class RhythmRuler { '" alt="' + _("Play all") + '" height="' + - RhythmRuler.ICONSIZE + + RhythmRuler.ICONSIZE + '" width="' + - RhythmRuler.ICONSIZE + + RhythmRuler.ICONSIZE + '" vertical-align="middle">'; for (let r = 0; r < this.Rulers.length; r++) { this._rulerSelected = r; @@ -1539,18 +1376,16 @@ class RhythmRuler { * @private * @returns {void} */ - __pause() { - this._playAllCell.innerHTML = '' +
             _('; this._playing = false; this._playingAll = false; @@ -1566,7 +1401,6 @@ class RhythmRuler { * @public * @returns {void} */ - playAll() { // External call from run button. if (this._playing) { @@ -1574,7 +1408,7 @@ class RhythmRuler { this.__pause(); // Wait for pause to complete before restarting. this._playingAll = true; - + setTimeout(() => { this.__resume(); }, 1000); @@ -1588,18 +1422,16 @@ class RhythmRuler { * @private * @returns {void} */ - __resume() { - this._playAllCell.innerHTML = '' +
             _('; logo.turtleDelay = 0; this._playingAll = true; @@ -1619,7 +1451,6 @@ class RhythmRuler { * @private * @returns {void} */ - _playAll() { logo.synth.stop(); logo.resetSynth(0); @@ -1641,7 +1472,6 @@ class RhythmRuler { * @private * @returns {void} */ - _playOne() { logo.synth.stop(); logo.resetSynth(0); @@ -1663,7 +1493,6 @@ class RhythmRuler { * @param {number} colIndex * @returns {void} */ - __loop(noteTime, rulerNo, colIndex) { const ruler = this._rulers[rulerNo]; if (ruler === null) { @@ -1685,8 +1514,7 @@ class RhythmRuler { if (this.Drums[rulerNo] === null) { drum = "snare drum"; } else { - const drumblockno = logo.blocks.blockList[this.Drums[rulerNo]] - .connections[1]; + const drumblockno = logo.blocks.blockList[this.Drums[rulerNo]].connections[1]; drum = logo.blocks.blockList[drumblockno].value; } @@ -1717,18 +1545,27 @@ class RhythmRuler { } } - - if (this._playing) { // Play the current note. if (noteValue > 0) { if (foundVoice) { logo.synth.trigger( - 0, "C4", Singer.defaultBPMFactor / noteValue, drum, null, null, false + 0, + "C4", + Singer.defaultBPMFactor / noteValue, + drum, + null, + null, + false ); } else if (foundDrum) { logo.synth.trigger( - 0, ["C4"], Singer.defaultBPMFactor / noteValue, drum, null, null + 0, + ["C4"], + Singer.defaultBPMFactor / noteValue, + drum, + null, + null ); } } @@ -1738,8 +1575,7 @@ class RhythmRuler { // Calculate any offset in playback. const d = new Date(); - this._offsets[rulerNo] = - d.getTime() - this._startingTime - this._elapsedTimes[rulerNo]; + this._offsets[rulerNo] = d.getTime() - this._startingTime - this._elapsedTimes[rulerNo]; } setTimeout(() => { @@ -1762,10 +1598,9 @@ class RhythmRuler { * @param {number} selectedRuler * @returns {void} */ - _save(selectedRuler) { // Deprecated -- replaced by save tuplets code - + for (const name in logo.blocks.palettes.dict) { logo.blocks.palettes.dict[name].hideMenu(true); } @@ -1783,30 +1618,20 @@ class RhythmRuler { } else { stack_value = logo.blocks.blockList[ - logo.blocks.blockList[this.Drums[selectedRuler]] - .connections[1] + logo.blocks.blockList[this.Drums[selectedRuler]].connections[1] ].value.split(" ")[0] + " " + _("rhythm"); } const delta = selectedRuler * 42; const newStack = [ - [ - 0, - ["action", { collapsed: true }], - 100 + delta, - 100 + delta, - [null, 1, 2, null] - ], + [0, ["action", { collapsed: true }], 100 + delta, 100 + delta, [null, 1, 2, null]], [1, ["text", { value: stack_value }], 0, 0, [0]] ]; let previousBlock = 0; let sameNoteValue = 1; for (let i = 0; i < ruler.cells.length; i++) { - if ( - noteValues[i] === noteValues[i + 1] && - i < ruler.cells.length - 1 - ) { + if (noteValues[i] === noteValues[i + 1] && i < ruler.cells.length - 1) { sameNoteValue += 1; continue; } else { @@ -1822,51 +1647,15 @@ class RhythmRuler { 0, [previousBlock, idx + 1, idx + 2, idx + 5] ]); - newStack.push([ - idx + 1, - ["number", { value: sameNoteValue }], - 0, - 0, - [idx] - ]); - newStack.push([ - idx + 2, - "divide", - 0, - 0, - [idx, idx + 3, idx + 4] - ]); - newStack.push([ - idx + 3, - ["number", { value: obj[0] }], - 0, - 0, - [idx + 2] - ]); - newStack.push([ - idx + 4, - ["number", { value: obj[1] }], - 0, - 0, - [idx + 2] - ]); + newStack.push([idx + 1, ["number", { value: sameNoteValue }], 0, 0, [idx]]); + newStack.push([idx + 2, "divide", 0, 0, [idx, idx + 3, idx + 4]]); + newStack.push([idx + 3, ["number", { value: obj[0] }], 0, 0, [idx + 2]]); + newStack.push([idx + 4, ["number", { value: obj[1] }], 0, 0, [idx + 2]]); newStack.push([idx + 5, "vspace", 0, 0, [idx, idx + 6]]); if (i == ruler.cells.length - 1) { - newStack.push([ - idx + 6, - "hidden", - 0, - 0, - [idx + 5, null] - ]); + newStack.push([idx + 6, "hidden", 0, 0, [idx + 5, null]]); } else { - newStack.push([ - idx + 6, - "hidden", - 0, - 0, - [idx + 5, idx + 7] - ]); + newStack.push([idx + 6, "hidden", 0, 0, [idx + 5, idx + 7]]); } previousBlock = idx + 6; @@ -1888,10 +1677,7 @@ class RhythmRuler { * @param {number} selectedRuler * @returns {void} */ - - _saveTuplets(selectedRuler) { - for (const name in logo.blocks.palettes.dict) { logo.blocks.palettes.dict[name].hideMenu(true); } @@ -1907,30 +1693,20 @@ class RhythmRuler { } else { stack_value = logo.blocks.blockList[ - logo.blocks.blockList[this.Drums[selectedRuler]] - .connections[1] + logo.blocks.blockList[this.Drums[selectedRuler]].connections[1] ].value.split(" ")[0] + " " + _("rhythm"); } const delta = selectedRuler * 42; const newStack = [ - [ - 0, - ["action", { collapsed: true }], - 100 + delta, - 100 + delta, - [null, 1, 2, null] - ], + [0, ["action", { collapsed: true }], 100 + delta, 100 + delta, [null, 1, 2, null]], [1, ["text", { value: stack_value }], 0, 0, [0]] ]; let previousBlock = 0; let sameNoteValue = 1; for (let i = 0; i < ruler.cells.length; i++) { - if ( - noteValues[i] === noteValues[i + 1] && - i < ruler.cells.length - 1 - ) { + if (noteValues[i] === noteValues[i + 1] && i < ruler.cells.length - 1) { sameNoteValue += 1; continue; } else { @@ -1946,41 +1722,11 @@ class RhythmRuler { 0, [previousBlock, idx + 1, idx + 2, idx + 5] ]); - newStack.push([ - idx + 1, - ["number", { value: sameNoteValue }], - 0, - 0, - [idx] - ]); - newStack.push([ - idx + 2, - "divide", - 0, - 0, - [idx, idx + 3, idx + 4] - ]); - newStack.push([ - idx + 3, - ["number", { value: obj[0] }], - 0, - 0, - [idx + 2] - ]); - newStack.push([ - idx + 4, - ["number", { value: n }], - 0, - 0, - [idx + 2] - ]); - newStack.push([ - idx + 5, - "vspace", - 0, - 0, - [idx, idx + 6] - ]); + newStack.push([idx + 1, ["number", { value: sameNoteValue }], 0, 0, [idx]]); + newStack.push([idx + 2, "divide", 0, 0, [idx, idx + 3, idx + 4]]); + newStack.push([idx + 3, ["number", { value: obj[0] }], 0, 0, [idx + 2]]); + newStack.push([idx + 4, ["number", { value: n }], 0, 0, [idx + 2]]); + newStack.push([idx + 5, "vspace", 0, 0, [idx, idx + 6]]); } else { newStack.push([ idx, @@ -1989,59 +1735,17 @@ class RhythmRuler { 0, [previousBlock, idx + 1, idx + 2, idx + 5] ]); - newStack.push([ - idx + 1, - ["number", { value: sameNoteValue }], - 0, - 0, - [idx] - ]); - newStack.push([ - idx + 2, - "divide", - 0, - 0, - [idx, idx + 3, idx + 4] - ]); - newStack.push([ - idx + 3, - ["number", { value: obj[0] }], - 0, - 0, - [idx + 2] - ]); - newStack.push([ - idx + 4, - ["number", { value: obj[1] }], - 0, - 0, - [idx + 2] - ]); - newStack.push([ - idx + 5, - "vspace", - 0, - 0, - [idx, idx + 6] - ]); + newStack.push([idx + 1, ["number", { value: sameNoteValue }], 0, 0, [idx]]); + newStack.push([idx + 2, "divide", 0, 0, [idx, idx + 3, idx + 4]]); + newStack.push([idx + 3, ["number", { value: obj[0] }], 0, 0, [idx + 2]]); + newStack.push([idx + 4, ["number", { value: obj[1] }], 0, 0, [idx + 2]]); + newStack.push([idx + 5, "vspace", 0, 0, [idx, idx + 6]]); } if (i == ruler.cells.length - 1) { - newStack.push([ - idx + 6, - "hidden", - 0, - 0, - [idx + 5, null] - ]); + newStack.push([idx + 6, "hidden", 0, 0, [idx + 5, null]]); } else { - newStack.push([ - idx + 6, - "hidden", - 0, - 0, - [idx + 5, idx + 7] - ]); + newStack.push([idx + 6, "hidden", 0, 0, [idx + 5, idx + 7]]); } previousBlock = idx + 6; @@ -2063,9 +1767,7 @@ class RhythmRuler { * @param {number} selectedRuler * @returns {void} */ - _saveTupletsMerged(noteValues) { - for (const name in logo.blocks.palettes.dict) { logo.blocks.palettes.dict[name].hideMenu(true); } @@ -2075,75 +1777,30 @@ class RhythmRuler { const stack_value = _("rhythm"); const delta = 42; const newStack = [ - [ - 0, - ["action", { collapsed: true }], - 100 + delta, - 100 + delta, - [null, 1, 2, null] - ], + [0, ["action", { collapsed: true }], 100 + delta, 100 + delta, [null, 1, 2, null]], [1, ["text", { value: stack_value }], 0, 0, [0]] ]; let previousBlock = 0; let sameNoteValue = 1; for (let i = 0; i < noteValues.length; i++) { - if ( - noteValues[i] === noteValues[i + 1] && - i < noteValues.length - 1 - ) { + if (noteValues[i] === noteValues[i + 1] && i < noteValues.length - 1) { sameNoteValue += 1; continue; } else { const idx = newStack.length; const noteValue = noteValues[i]; const obj = rationalToFraction(1 / Math.abs(noteValue)); - newStack.push([ - idx, - "rhythm2", - 0, - 0, - [previousBlock, idx + 1, idx + 2, idx + 5] - ]); - newStack.push([ - idx + 1, - ["number", { value: sameNoteValue }], - 0, - 0, - [idx] - ]); - newStack.push([ - idx + 2, - "divide", - 0, - 0, - [idx, idx + 3, idx + 4] - ]); - newStack.push([ - idx + 3, - ["number", { value: obj[0] }], - 0, - 0, - [idx + 2] - ]); - newStack.push([ - idx + 4, - ["number", { value: obj[1] }], - 0, - 0, - [idx + 2] - ]); + newStack.push([idx, "rhythm2", 0, 0, [previousBlock, idx + 1, idx + 2, idx + 5]]); + newStack.push([idx + 1, ["number", { value: sameNoteValue }], 0, 0, [idx]]); + newStack.push([idx + 2, "divide", 0, 0, [idx, idx + 3, idx + 4]]); + newStack.push([idx + 3, ["number", { value: obj[0] }], 0, 0, [idx + 2]]); + newStack.push([idx + 4, ["number", { value: obj[1] }], 0, 0, [idx + 2]]); newStack.push([idx + 5, "vspace", 0, 0, [idx, idx + 6]]); if (i == noteValues.length - 1) { newStack.push([idx + 6, "hidden", 0, 0, [idx + 5, null]]); } else { - newStack.push([ - idx + 6, - "hidden", - 0, - 0, - [idx + 5, idx + 7] - ]); + newStack.push([idx + 6, "hidden", 0, 0, [idx + 5, idx + 7]]); } previousBlock = idx + 6; @@ -2160,16 +1817,13 @@ class RhythmRuler { * @param {number} selectedRuler * @returns {void} */ - - _saveMachine (selectedRuler) { + _saveMachine(selectedRuler) { // We are either saving a drum machine or a voice machine. let drum; if (this.Drums[selectedRuler] === null) { drum = "snare drum"; } else { - const drumBlockNo = logo.blocks.blockList[ - this.Drums[selectedRuler] - ].connections[1]; + const drumBlockNo = logo.blocks.blockList[this.Drums[selectedRuler]].connections[1]; drum = logo.blocks.blockList[drumBlockNo].value; } @@ -2200,9 +1854,7 @@ class RhythmRuler { * @param {boolean} effect * @returns {void} */ - _saveDrumMachine(selectedRuler, drum, effect) { - for (const name in logo.blocks.palettes.dict) { logo.blocks.palettes.dict[name].hideMenu(true); } @@ -2223,30 +1875,20 @@ class RhythmRuler { } else { action_name = logo.blocks.blockList[ - logo.blocks.blockList[this.Drums[selectedRuler]] - .connections[1] + logo.blocks.blockList[this.Drums[selectedRuler]].connections[1] ].value.split(" ")[0] + " " + _("action"); } const newStack = [ - [ - 0, - ["action", { collapsed: true }], - 100 + delta, - 100 + delta, - [null, 1, 2, null] - ], + [0, ["action", { collapsed: true }], 100 + delta, 100 + delta, [null, 1, 2, null]], [1, ["text", { value: action_name }], 0, 0, [0]] ]; let previousBlock = 0; // 1 let sameNoteValue = 1; for (let i = 0; i < ruler.cells.length; i++) { - if ( - noteValues[i] === noteValues[i + 1] && - i < ruler.cells.length - 1 - ) { + if (noteValues[i] === noteValues[i + 1] && i < ruler.cells.length - 1) { sameNoteValue += 1; continue; } else { @@ -2264,20 +1906,8 @@ class RhythmRuler { 0, [previousBlock, idx + 1, idx + 4, idx + 7] ]); - newStack.push([ - idx + 1, - "divide", - 0, - 0, - [idx, idx + 2, idx + 3] - ]); - newStack.push([ - idx + 2, - ["number", { value: obj[0] }], - 0, - 0, - [idx + 1] - ]); + newStack.push([idx + 1, "divide", 0, 0, [idx, idx + 2, idx + 3]]); + newStack.push([idx + 2, ["number", { value: obj[0] }], 0, 0, [idx + 1]]); if (noteValue < 0) { newStack.push([ idx + 3, @@ -2286,27 +1916,9 @@ class RhythmRuler { 0, [idx + 1] ]); - newStack.push([ - idx + 4, - "vspace", - 0, - 0, - [idx, idx + 5] - ]); - newStack.push([ - idx + 5, - "rest2", - 0, - 0, - [idx + 4, idx + 6] - ]); - newStack.push([ - idx + 6, - "hidden", - 0, - 0, - [idx + 5, null] - ]); + newStack.push([idx + 4, "vspace", 0, 0, [idx, idx + 5]]); + newStack.push([idx + 5, "rest2", 0, 0, [idx + 4, idx + 6]]); + newStack.push([idx + 6, "hidden", 0, 0, [idx + 5, null]]); } else { newStack.push([ idx + 3, @@ -2315,20 +1927,8 @@ class RhythmRuler { 0, [idx + 1] ]); - newStack.push([ - idx + 4, - "vspace", - 0, - 0, - [idx, idx + 5] - ]); - newStack.push([ - idx + 5, - "playdrum", - 0, - 0, - [idx + 4, idx + 6, null] - ]); + newStack.push([idx + 4, "vspace", 0, 0, [idx, idx + 5]]); + newStack.push([idx + 5, "playdrum", 0, 0, [idx + 4, idx + 6, null]]); if (effect) { newStack.push([ idx + 6, @@ -2348,21 +1948,9 @@ class RhythmRuler { } } if (i == ruler.cells.length - 1) { - newStack.push([ - idx + 7, - "hidden", - 0, - 0, - [idx, null] - ]); + newStack.push([idx + 7, "hidden", 0, 0, [idx, null]]); } else { - newStack.push([ - idx + 7, - "hidden", - 0, - 0, - [idx, idx + 8] - ]); + newStack.push([idx + 7, "hidden", 0, 0, [idx, idx + 8]]); previousBlock = idx + 7; } } else { @@ -2385,34 +1973,10 @@ class RhythmRuler { ]); previousBlock = idx; } - newStack.push([ - idx + 1, - ["number", { value: sameNoteValue }], - 0, - 0, - [idx] - ]); - newStack.push([ - idx + 2, - "newnote", - 0, - 0, - [idx, idx + 3, idx + 6, idx + 9] - ]); - newStack.push([ - idx + 3, - "divide", - 0, - 0, - [idx + 2, idx + 4, idx + 5] - ]); - newStack.push([ - idx + 4, - ["number", { value: 1 }], - 0, - 0, - [idx + 3] - ]); + newStack.push([idx + 1, ["number", { value: sameNoteValue }], 0, 0, [idx]]); + newStack.push([idx + 2, "newnote", 0, 0, [idx, idx + 3, idx + 6, idx + 9]]); + newStack.push([idx + 3, "divide", 0, 0, [idx + 2, idx + 4, idx + 5]]); + newStack.push([idx + 4, ["number", { value: 1 }], 0, 0, [idx + 3]]); if (noteValue < 0) { newStack.push([ idx + 5, @@ -2421,27 +1985,9 @@ class RhythmRuler { 0, [idx + 3] ]); - newStack.push([ - idx + 6, - "vspace", - 0, - 0, - [idx + 2, idx + 7] - ]); - newStack.push([ - idx + 7, - "rest2", - 0, - 0, - [idx + 6, idx + 8] - ]); - newStack.push([ - idx + 8, - "hidden", - 0, - 0, - [idx + 7, null] - ]); + newStack.push([idx + 6, "vspace", 0, 0, [idx + 2, idx + 7]]); + newStack.push([idx + 7, "rest2", 0, 0, [idx + 6, idx + 8]]); + newStack.push([idx + 8, "hidden", 0, 0, [idx + 7, null]]); } else { newStack.push([ idx + 5, @@ -2450,20 +1996,8 @@ class RhythmRuler { 0, [idx + 3] ]); - newStack.push([ - idx + 6, - "vspace", - 0, - 0, - [idx + 2, idx + 7] - ]); - newStack.push([ - idx + 7, - "playdrum", - 0, - 0, - [idx + 6, idx + 8, null] - ]); + newStack.push([idx + 6, "vspace", 0, 0, [idx + 2, idx + 7]]); + newStack.push([idx + 7, "playdrum", 0, 0, [idx + 6, idx + 8, null]]); if (effect) { newStack.push([ idx + 8, @@ -2482,13 +2016,7 @@ class RhythmRuler { ]); } } - newStack.push([ - idx + 9, - "hidden", - 0, - 0, - [idx + 2, null] - ]); + newStack.push([idx + 9, "hidden", 0, 0, [idx + 2, null]]); } sameNoteValue = 1; @@ -2511,9 +2039,7 @@ class RhythmRuler { * @param {string} voice * @returns {void} */ - _saveVoiceMachine(selectedRuler, voice) { - for (const name in logo.blocks.palettes.dict) { logo.blocks.palettes.dict[name].hideMenu(true); } @@ -2540,21 +2066,14 @@ class RhythmRuler { } else { action_name = logo.blocks.blockList[ - logo.blocks.blockList[this.Drums[selectedRuler]] - .connections[1] + logo.blocks.blockList[this.Drums[selectedRuler]].connections[1] ].value.split(" ")[0] + "_" + _("action"); } const newStack = [ - [ - 0, - ["action", { collapsed: true }], - 100 + delta, - 100 + delta, - [null, 1, 2, null] - ], + [0, ["action", { collapsed: true }], 100 + delta, 100 + delta, [null, 1, 2, null]], [1, ["text", { value: action_name }], 0, 0, [0]] ]; newStack.push([2, "settimbre", 0, 0, [0, 3, 5, 4]]); @@ -2563,10 +2082,7 @@ class RhythmRuler { let previousBlock = 2; let sameNoteValue = 1; for (let i = 0; i < ruler.cells.length; i++) { - if ( - noteValues[i] === noteValues[i + 1] && - i < ruler.cells.length - 1 - ) { + if (noteValues[i] === noteValues[i + 1] && i < ruler.cells.length - 1) { sameNoteValue += 1; continue; } else { @@ -2585,13 +2101,7 @@ class RhythmRuler { 0, [previousBlock, idx + 1, idx + 4, idx + 7] ]); - newStack.push([ - idx + 1, - "divide", - 0, - 0, - [idx, idx + 2, idx + 3] - ]); + newStack.push([idx + 1, "divide", 0, 0, [idx, idx + 2, idx + 3]]); newStack.push([ idx + 2, ["number", { value: obj[0] }], @@ -2606,43 +2116,13 @@ class RhythmRuler { 0, [idx + 1] ]); - newStack.push([ - idx + 4, - "vspace", - 0, - 0, - [idx, idx + 5] - ]); - newStack.push([ - idx + 5, - "rest2", - 0, - 0, - [idx + 4, idx + 6] - ]); - newStack.push([ - idx + 6, - "hidden", - 0, - 0, - [idx + 5, null] - ]); + newStack.push([idx + 4, "vspace", 0, 0, [idx, idx + 5]]); + newStack.push([idx + 5, "rest2", 0, 0, [idx + 4, idx + 6]]); + newStack.push([idx + 6, "hidden", 0, 0, [idx + 5, null]]); if (i == ruler.cells.length - 1) { - newStack.push([ - idx + 7, - "hidden", - 0, - 0, - [idx, null] - ]); + newStack.push([idx + 7, "hidden", 0, 0, [idx, null]]); } else { - newStack.push([ - idx + 7, - "hidden", - 0, - 0, - [idx, idx + 8] - ]); + newStack.push([idx + 7, "hidden", 0, 0, [idx, idx + 8]]); previousBlock = idx + 7; } } else { @@ -2653,13 +2133,7 @@ class RhythmRuler { 0, [previousBlock, idx + 1, idx + 4, idx + 8] ]); - newStack.push([ - idx + 1, - "divide", - 0, - 0, - [idx, idx + 2, idx + 3] - ]); + newStack.push([idx + 1, "divide", 0, 0, [idx, idx + 2, idx + 3]]); newStack.push([ idx + 2, ["number", { value: obj[0] }], @@ -2674,13 +2148,7 @@ class RhythmRuler { 0, [idx + 1] ]); - newStack.push([ - idx + 4, - "vspace", - 0, - 0, - [idx, idx + 5] - ]); + newStack.push([idx + 4, "vspace", 0, 0, [idx, idx + 5]]); newStack.push([ idx + 5, "pitch", @@ -2688,36 +2156,12 @@ class RhythmRuler { 0, [idx + 4, idx + 6, idx + 7, null] ]); - newStack.push([ - idx + 6, - ["notename", { value: "C" }], - 0, - 0, - [idx + 5] - ]); - newStack.push([ - idx + 7, - ["number", { value: 4 }], - 0, - 0, - [idx + 5] - ]); + newStack.push([idx + 6, ["notename", { value: "C" }], 0, 0, [idx + 5]]); + newStack.push([idx + 7, ["number", { value: 4 }], 0, 0, [idx + 5]]); if (i == ruler.cells.length - 1) { - newStack.push([ - idx + 8, - "hidden", - 0, - 0, - [idx, null] - ]); + newStack.push([idx + 8, "hidden", 0, 0, [idx, null]]); } else { - newStack.push([ - idx + 8, - "hidden", - 0, - 0, - [idx, idx + 9] - ]); + newStack.push([idx + 8, "hidden", 0, 0, [idx, idx + 9]]); previousBlock = idx + 8; } } @@ -2741,13 +2185,7 @@ class RhythmRuler { ]); previousBlock = idx; } - newStack.push([ - idx + 1, - ["number", { value: sameNoteValue }], - 0, - 0, - [idx] - ]); + newStack.push([idx + 1, ["number", { value: sameNoteValue }], 0, 0, [idx]]); if (noteValue < 0) { newStack.push([ idx + 2, @@ -2756,20 +2194,8 @@ class RhythmRuler { 0, [idx, idx + 3, idx + 6, idx + 9] ]); - newStack.push([ - idx + 3, - "divide", - 0, - 0, - [idx + 2, idx + 4, idx + 5] - ]); - newStack.push([ - idx + 4, - ["number", { value: 1 }], - 0, - 0, - [idx + 3] - ]); + newStack.push([idx + 3, "divide", 0, 0, [idx + 2, idx + 4, idx + 5]]); + newStack.push([idx + 4, ["number", { value: 1 }], 0, 0, [idx + 3]]); newStack.push([ idx + 5, ["number", { value: -noteValue }], @@ -2777,34 +2203,10 @@ class RhythmRuler { 0, [idx + 3] ]); - newStack.push([ - idx + 6, - "vspace", - 0, - 0, - [idx + 2, idx + 7] - ]); - newStack.push([ - idx + 7, - "rest2", - 0, - 0, - [idx + 6, idx + 8] - ]); - newStack.push([ - idx + 8, - "hidden", - 0, - 0, - [idx + 7, null] - ]); - newStack.push([ - idx + 9, - "hidden", - 0, - 0, - [idx + 2, null] - ]); + newStack.push([idx + 6, "vspace", 0, 0, [idx + 2, idx + 7]]); + newStack.push([idx + 7, "rest2", 0, 0, [idx + 6, idx + 8]]); + newStack.push([idx + 8, "hidden", 0, 0, [idx + 7, null]]); + newStack.push([idx + 9, "hidden", 0, 0, [idx + 2, null]]); } else { newStack.push([ idx + 2, @@ -2813,20 +2215,8 @@ class RhythmRuler { 0, [idx, idx + 3, idx + 6, idx + 10] ]); - newStack.push([ - idx + 3, - "divide", - 0, - 0, - [idx + 2, idx + 4, idx + 5] - ]); - newStack.push([ - idx + 4, - ["number", { value: 1 }], - 0, - 0, - [idx + 3] - ]); + newStack.push([idx + 3, "divide", 0, 0, [idx + 2, idx + 4, idx + 5]]); + newStack.push([idx + 4, ["number", { value: 1 }], 0, 0, [idx + 3]]); newStack.push([ idx + 5, ["number", { value: noteValue }], @@ -2834,13 +2224,7 @@ class RhythmRuler { 0, [idx + 3] ]); - newStack.push([ - idx + 6, - "vspace", - 0, - 0, - [idx + 2, idx + 7] - ]); + newStack.push([idx + 6, "vspace", 0, 0, [idx + 2, idx + 7]]); newStack.push([ idx + 7, "pitch", @@ -2848,27 +2232,9 @@ class RhythmRuler { 0, [idx + 6, idx + 8, idx + 9, null] ]); - newStack.push([ - idx + 8, - ["notename", { value: "C" }], - 0, - 0, - [idx + 7] - ]); - newStack.push([ - idx + 9, - ["number", { value: 4 }], - 0, - 0, - [idx + 7] - ]); - newStack.push([ - idx + 10, - "hidden", - 0, - 0, - [idx + 2, null] - ]); + newStack.push([idx + 8, ["notename", { value: "C" }], 0, 0, [idx + 7]]); + newStack.push([idx + 9, ["number", { value: 4 }], 0, 0, [idx + 7]]); + newStack.push([idx + 10, "hidden", 0, 0, [idx + 2, null]]); } } @@ -2889,7 +2255,6 @@ class RhythmRuler { * @private * @returns {array} */ - _mergeRulers() { // Merge the rulers into one set of rhythms. const rList = []; @@ -2926,7 +2291,6 @@ class RhythmRuler { * @private * @returns {boolean} */ - _get_save_lock() { return this._save_lock; } @@ -2935,10 +2299,9 @@ class RhythmRuler { * @public * @returns {void} */ - saveDissectHistory() { // Save the new dissect history. - + const dissectHistory = []; const drums = []; let drum; @@ -2962,9 +2325,7 @@ class RhythmRuler { for (let i = 0; i < this._dissectHistory.length; i++) { drum = this._dissectHistory[i][1]; if (drums.indexOf(drum) === -1) { - history = JSON.parse( - JSON.stringify(this._dissectHistory[i][0]) - ); + history = JSON.parse(JSON.stringify(this._dissectHistory[i][0])); dissectHistory.push([history, drum]); } } @@ -3021,13 +2382,10 @@ class RhythmRuler { */ } - /** * @private * @returns {void} */ - - _positionWheel() { if (docById("wheelDiv").style.display == "none") { return; @@ -3053,4 +2411,4 @@ class RhythmRuler { docById("wheelDiv").style.top = y - 300 + "px"; } } -} \ No newline at end of file +} From 314615538cfcf2610d1cd74176ddad44316ae034 Mon Sep 17 00:00:00 2001 From: Anindya Kundu Date: Sun, 31 Jan 2021 19:15:01 +0530 Subject: [PATCH 7/7] Reverse ESLint disabling --- js/blocks/WidgetBlocks.js | 1 - 1 file changed, 1 deletion(-) diff --git a/js/blocks/WidgetBlocks.js b/js/blocks/WidgetBlocks.js index 7d876e5b48..a1673ba059 100644 --- a/js/blocks/WidgetBlocks.js +++ b/js/blocks/WidgetBlocks.js @@ -1,4 +1,3 @@ -/* eslint-disable no-undef */ function setupWidgetBlocks() { class EnvelopeBlock extends FlowBlock { constructor() {