Skip to content

Commit e0856d2

Browse files
committed
Added bar lines to phrase-maker based on meter block
1 parent 26e0c54 commit e0856d2

File tree

1 file changed

+89
-1
lines changed

1 file changed

+89
-1
lines changed

js/widgets/phrasemaker.js

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,20 @@ class PhraseMaker {
264264
* @type {boolean}
265265
*/
266266
this.lyricsON = false;
267+
268+
/**
269+
* Number of beats per measure (from meter block)
270+
* @type {number}
271+
* @private
272+
*/
273+
this._meterBeats = 4;
274+
275+
/**
276+
* Note value that gets the beat (from meter block)
277+
* @type {number}
278+
* @private
279+
*/
280+
this._meterNoteValue = 4;
267281
}
268282

269283
/**
@@ -357,6 +371,67 @@ class PhraseMaker {
357371
}
358372
}
359373
}
374+
/**
375+
* Gets the current meter settings from the turtle's singer.
376+
* Updates internal meter properties for bar line calculations.
377+
* @private
378+
*/
379+
_getCurrentMeter() {
380+
// Access meter from the turtle's singer
381+
if (this.activity.turtles && this.activity.turtles.ithTurtle(0)) {
382+
const turtle = this.activity.turtles.ithTurtle(0);
383+
if (turtle.singer) {
384+
// Get the most recent meter setting
385+
if (turtle.singer.beatsPerMeasure && turtle.singer.beatsPerMeasure.length > 0) {
386+
this._meterBeats = turtle.singer.beatsPerMeasure[turtle.singer.beatsPerMeasure.length - 1];
387+
}
388+
if (turtle.singer.noteValuePerBeat && turtle.singer.noteValuePerBeat.length > 0) {
389+
this._meterNoteValue = turtle.singer.noteValuePerBeat[turtle.singer.noteValuePerBeat.length - 1];
390+
}
391+
}
392+
}
393+
}
394+
395+
/**
396+
* Determines if a column position should have a bar line.
397+
* @param {number} columnIndex - The index of the column to check.
398+
* @returns {boolean} True if this column should have a bar line.
399+
* @private
400+
*/
401+
_isBarLine(columnIndex) {
402+
if (columnIndex === 0) {
403+
return true; // First measure always starts with a bar line
404+
}
405+
406+
this._getCurrentMeter();
407+
408+
// Calculate the cumulative note value up to this position
409+
let cumulativeValue = 0;
410+
for (let i = 0; i < columnIndex; i++) {
411+
if (this._noteValueRow && this._noteValueRow.cells[i]) {
412+
const noteValue = parseFloat(this._noteValueRow.cells[i].getAttribute("alt")) || 0;
413+
cumulativeValue += noteValue;
414+
} else {
415+
// Fallback for tuplet cases
416+
const noteValue = 1 / (this._notesToPlay[i] && this._notesToPlay[i][1] ? this._notesToPlay[i][1] : 4);
417+
cumulativeValue += noteValue;
418+
}
419+
}
420+
421+
// Calculate measure duration in whole note units
422+
// meterBeats * (4 / meterNoteValue) gives us the measure duration
423+
const measureDuration = this._meterBeats * (4 / this._meterNoteValue);
424+
425+
// Convert cumulative value to quarter note units for easier calculation
426+
const positionInQuarters = cumulativeValue * 4;
427+
const measureInQuarters = measureDuration;
428+
429+
// Check if we're at a measure boundary (with tolerance for floating point errors)
430+
const remainder = positionInQuarters % measureInQuarters;
431+
432+
433+
return Math.abs(remainder) < 0.001 || Math.abs(remainder - measureInQuarters) < 0.001;
434+
}
360435

361436
/**
362437
* Clears block references within the PhraseMaker.
@@ -504,6 +579,8 @@ class PhraseMaker {
504579
this._notesToPlay = [];
505580
this._matrixHasTuplets = false;
506581

582+
this._getCurrentMeter();
583+
507584
// Add the buttons to the top row.
508585

509586
widgetWindow.onclose = () => {
@@ -3024,7 +3101,7 @@ class PhraseMaker {
30243101
cell.innerHTML = tupletValue;
30253102
cell.style.backgroundColor = platformColor.tupletBackground;
30263103

3027-
// And a span in the note value column too.
3104+
// Add a span in the note value column too.
30283105
const noteValueRow = this._noteValueRow;
30293106
cell = noteValueRow.insertCell();
30303107
cell.colSpan = numberOfNotes;
@@ -3038,6 +3115,12 @@ class PhraseMaker {
30383115
cell.innerHTML = noteValueToDisplay;
30393116
cell.style.backgroundColor = platformColor.rhythmcellcolor;
30403117
this._matrixHasTuplets = true;
3118+
3119+
const currentColumnIndex = noteValueRow.cells.length - 1;
3120+
if (this._isBarLine(currentColumnIndex)) {
3121+
cell.style.borderLeft = "3px solid #333333";
3122+
cell.style.paddingLeft = "2px";
3123+
}
30413124
}
30423125

30433126
/**
@@ -3127,6 +3210,11 @@ class PhraseMaker {
31273210
cell.style.color = platformColor.textColor;
31283211
cell.setAttribute("alt", noteValue);
31293212

3213+
if (this._isBarLine(j)) {
3214+
cell.style.borderLeft = "3px solid #333333";
3215+
cell.style.paddingLeft = "2px";
3216+
}
3217+
31303218
if (this._matrixHasTuplets) {
31313219
// We may need to insert some blank cells in the extra rows
31323220
// added by tuplets.

0 commit comments

Comments
 (0)