strumming-metronome
Version:
A simple JavaScript metronome for strumming practice.
933 lines (899 loc) • 30.6 kB
JavaScript
const metronomeTables = {
44: `
<table id="measure-table">
<tr>
<td colspan="4">
<span id="beat1" class="metronome-beat"><strong>1</strong></span>
</td>
<td colspan="4">
<span id="beat2" class="metronome-beat"><strong>2</strong></span>
</td>
<td colspan="4">
<span id="beat3" class="metronome-beat"><strong>3</strong></span>
</td>
<td colspan="4">
<span id="beat4" class="metronome-beat"><strong>4</strong></span>
</td>
</tr>
<tr>
<td>
<button id="down-1.0" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-1.25" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-1.50" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-1.75"class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-2.0" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-2.25" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-2.50" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-2.75" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-3.0" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-3.25" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-3.50" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-3.75" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-4.0" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-4.25" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-4.50" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-4.75" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
</tr>
<tr>
<td colspan="16" class="strings-cell">
<hr class="guitar-string-6">
<hr class="guitar-string-5">
<hr class="guitar-string-4">
<hr class="guitar-string-3">
<hr class="guitar-string-2">
<hr class="guitar-string-1">
</td>
</tr>
<tr>
<td>
<button id="up-1.0" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-1.25" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-1.50" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-1.75" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-2.0" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-2.25" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-2.50" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-2.75" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-3.0" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-3.25" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-3.50" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-3.75" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-4.0" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-4.25" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-4.50" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-4.75" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
</tr>
</table>
`,
34: `
<table id="measure-table">
<tr>
<td colspan="4">
<span id="beat1" class="metronome-beat"><strong>1</strong></span>
</td>
<td colspan="4">
<span id="beat2" class="metronome-beat"><strong>2</strong></span>
</td>
<td colspan="4">
<span id="beat3" class="metronome-beat"><strong>3</strong></span>
</td>
</tr>
<tr>
<td>
<button id="down-1.0" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-1.25" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-1.50" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-1.75"class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-2.0" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-2.25" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-2.50" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-2.75" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-3.0" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-3.25" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-3.50" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-3.75" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
</tr>
<tr>
<td colspan="16" class="strings-cell">
<hr class="guitar-string-6">
<hr class="guitar-string-5">
<hr class="guitar-string-4">
<hr class="guitar-string-3">
<hr class="guitar-string-2">
<hr class="guitar-string-1">
</td>
</tr>
<tr>
<td>
<button id="up-1.0" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-1.25" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-1.50" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-1.75" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-2.0" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-2.25" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-2.50" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-2.75" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-3.0" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-3.25" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-3.50" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-3.75" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
</tr>
</table>
`,
68: `
<table id="measure-table">
<tr>
<td colspan="2">
<span id="beat1" class="metronome-beat"><strong>1</strong></span>
</td>
<td colspan="2">
<span id="beat2" class="metronome-beat"><strong>2</strong></span>
</td>
<td colspan="2">
<span id="beat3" class="metronome-beat"><strong>3</strong></span>
</td>
<td colspan="2">
<span id="beat4" class="metronome-beat"><strong>4</strong></span>
</td>
<td colspan="2">
<span id="beat5" class="metronome-beat"><strong>5</strong></span>
</td>
<td colspan="2">
<span id="beat6" class="metronome-beat"><strong>6</strong></span>
</td>
</tr>
<tr>
<td>
<button id="down-1.0" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-1.50" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-2.0" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-2.50" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-3.0" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-3.50" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-4.0" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-4.50" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-5.0" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-5.50" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-6.0" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
<td>
<button id="down-6.50" class="metronome-arrow-down-btn">
<i class="metronome-arrow-down-black-icon"></i>
</button>
</td>
</tr>
<tr>
<td colspan="16" class="strings-cell">
<hr class="guitar-string-6">
<hr class="guitar-string-5">
<hr class="guitar-string-4">
<hr class="guitar-string-3">
<hr class="guitar-string-2">
<hr class="guitar-string-1">
</td>
</tr>
<tr>
<td>
<button id="up-1.0" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-1.50" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-2.0" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-2.50" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-3.0" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-3.50" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-4.0" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-4.50" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-5.0" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-5.50" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-6.0" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
<td>
<button id="up-6.50" class="metronome-arrow-up-btn">
<i class="metronome-arrow-up-black-icon"></i>
</button>
</td>
</tr>
</table>
`
};
const metronomeContainerHTML = `
<div id="metronome-controller">
<label for="metronome-time-signature">Time Signature:</label>
<select id="metronome-time-signature">
<option value="44">4/4</option>
<option value="34">3/4</option>
<option value="68">6/8</option>
</select>
<br/>
<input id="bpm-input" type="number" value="60" min="30" max="300">
<span class="bpm-unit">bpm</span>
<br/>
<button id="metronome-btn">
<i id="metronome-icon" class="metronome-play-icon"></i>
<span id="metronome-text">Play</span>
</button>
</div>
<div id="metronome-table">
</div>
`;
const metronomeCSS = `
#metronome-container {
padding: 10px;
overflow: auto; /* Enable scrolling if content overflows */
}
#bpm-input {
margin-top: 5px;
padding: 5px;
}
#metronome-time-signature {
padding: 5px;
}
.bpm-unit {
margin-right: 10px;
}
#metronome-btn {
margin-top: 10px;
padding: 5px;
background: #28a745;
color: #fff !important;
border: none;
cursor: pointer;
border-radius: 0.125rem;
}
#metronome-btn.running {
background: #dc3545;
}
#metronome-controller {
margin: 20px;
}
#measure-table {
margin: 20px;
}
.metronome-play-icon {
background: url("./assets/images/play.svg") no-repeat center;
display: inline-block;
width: 10px;
height: 10px;
}
.metronome-stop-icon {
background: url("./assets/images/stop.svg") no-repeat center;
display: inline-block;
width: 10px;
height: 10px;
}
.metronome-arrow-up-black-icon {
background: url("./assets/images/arrow-up-black.svg") no-repeat center;
display: inline-block;
width: 15px;
height: 15px;
}
.metronome-arrow-down-black-icon {
background: url("./assets/images/arrow-down-black.svg") no-repeat center;
display: inline-block;
width: 15px;
height: 15px;
}
.metronome-arrow-up-blue-icon {
background: url("./assets/images/arrow-up-blue.svg") no-repeat center;
display: inline-block;
width: 15px;
height: 15px;
}
.metronome-arrow-down-blue-icon {
background: url("./assets/images/arrow-down-blue.svg") no-repeat center;
display: inline-block;
width: 15px;
height: 15px;
}
.metronome-arrow-up-white-icon {
background: url("./assets/images/arrow-up-white.svg") no-repeat center;
display: inline-block;
width: 15px;
height: 15px;
}
.metronome-arrow-down-white-icon {
background: url("./assets/images/arrow-down-white.svg") no-repeat center;
display: inline-block;
width: 15px;
height: 15px;
}
.guitar-string-1 {
border-top: 0.25px solid #000; /* String 1 (thinnest) */
}
.guitar-string-2 {
border-top: 0.5px solid #000;
}
.guitar-string-3 {
border-top: 1px solid #000;
}
.guitar-string-4 {
border-top: 1.5px solid #000;
}
.guitar-string-5 {
border-top: 2px solid #000;
}
.guitar-string-6 {
border-top: 2.5px solid #000; /* String 6 (thickest) */
}
.metronome-beat {
margin-left: 10px;
}
.metronome-arrow-up-btn {
padding-top: 3px;
cursor: pointer;
background-color: #fff;
color: black;
border-width: 1px;
border-style: outset;
border-color: black;
border-radius: 0.125rem;
}
.metronome-arrow-down-btn {
padding-top: 3px;
cursor: pointer;
background-color: #fff;
color: black;
border-width: 1px;
border-style: outset;
border-color: black;
border-radius: 0.125rem;
}
.metronome-selected-button {
padding-top: 3px;
cursor: pointer;
background-color: #D3D3D3;
color: blue;
border-width: 1px;
border-style: outset;
border-color: blue;
border-radius: 0.125rem;
}
.metronome-active-btn {
background-color: blue;
color: white;
border-width: 1px;
border-style: outset;
border-color: blue;
border-radius: 0.125rem;
}
`;
const beatIds = {
44: ["beat1", "beat2", "beat3", "beat4"],
34: ["beat1", "beat2", "beat3"],
68: ["beat1", "beat2", "beat3", "beat4", "beat5", "beat6"]
}
const upQuarterBeatIds = {
44: [
"up-1.0", "up-1.25", "up-1.50", "up-1.75",
"up-2.0", "up-2.25", "up-2.50", "up-2.75",
"up-3.0", "up-3.25", "up-3.50", "up-3.75",
"up-4.0", "up-4.25", "up-4.50", "up-4.75"
],
34: [
"up-1.0", "up-1.25", "up-1.50", "up-1.75",
"up-2.0", "up-2.25", "up-2.50", "up-2.75",
"up-3.0", "up-3.25", "up-3.50", "up-3.75"
],
68: [
"up-1.0", "up-1.50",
"up-2.0", "up-2.50",
"up-3.0", "up-3.50",
"up-4.0", "up-4.50",
"up-5.0", "up-5.50",
"up-6.0", "up-6.50"
]
};
const downQuarterBeatIds = {
44: [
"down-1.0", "down-1.25", "down-1.50", "down-1.75",
"down-2.0", "down-2.25", "down-2.50", "down-2.75",
"down-3.0", "down-3.25", "down-3.50", "down-3.75",
"down-4.0", "down-4.25", "down-4.50", "down-4.75"
],
34: [
"down-1.0", "down-1.25", "down-1.50", "down-1.75",
"down-2.0", "down-2.25", "down-2.50", "down-2.75",
"down-3.0", "down-3.25", "down-3.50", "down-3.75"
],
68: [
"down-1.0", "down-1.50",
"down-2.0", "down-2.50",
"down-3.0", "down-3.50",
"down-4.0", "down-4.50",
"down-5.0", "down-5.50",
"down-6.0", "down-6.50"
]
}
const downStrum = new Audio("./assets/sounds/down-strum.mp3");
const upStrum = new Audio("./assets/sounds/up-strum.mp3");
function unlockAudio() {
const context = new (window.AudioContext || window.webkitAudioContext)();
const buffer = context.createBuffer(1, 1, 22050);
const source = context.createBufferSource();
source.buffer = buffer;
source.connect(context.destination);
source.start(0);
document.removeEventListener('touchstart', unlockAudio);
}
function selectMetronome(timeSelection) {
// Remove listeners for other time signatures
const oldButton = document.getElementById("metronome-btn");
const button = oldButton.cloneNode(true);
oldButton.parentNode.replaceChild(button, oldButton);
// Inject Table into the page
const metronomeTable = document.querySelector("#metronome-table");
metronomeTable.innerHTML = metronomeTables[`${timeSelection}`];
// Add event listeners to toggle the class btn classes
metronomeTable.querySelectorAll(".metronome-arrow-up-btn").forEach(button => {
button.addEventListener("click", () => {
button.classList.toggle("metronome-selected-button");
if (button.firstElementChild.className === "metronome-arrow-up-black-icon") {
button.firstElementChild.className = "metronome-arrow-up-blue-icon";
} else {
button.firstElementChild.className = "metronome-arrow-up-black-icon";
}
});
});
metronomeTable.querySelectorAll(".metronome-arrow-down-btn").forEach(button => {
button.addEventListener("click", () => {
button.classList.toggle("metronome-selected-button");
if (button.firstElementChild.className === "metronome-arrow-down-black-icon") {
button.firstElementChild.className = "metronome-arrow-down-blue-icon";
} else {
button.firstElementChild.className = "metronome-arrow-down-black-icon";
}
});
});
// Get references to elements
const container = document.querySelector("#metronome-container");
const bpmInput = container.querySelector("#bpm-input");
const icon = container.querySelector("#metronome-icon");
const text = container.querySelector("#metronome-text");
const timeSignature = container.querySelector("#metronome-time-signature");
// Store elements
const beats = beatIds[`${timeSelection}`].map(id => document.getElementById(id));
const upQuarterBeatButtons = upQuarterBeatIds[`${timeSelection}`].map(id => document.getElementById(id));
const downQuarterBeatButtons = downQuarterBeatIds[`${timeSelection}`].map(id => document.getElementById(id));
let currentBeatIndex = 0; // Track the current beat
let interval;
function playMetronome() {
if (interval) {
clearInterval(interval);
interval = null;
icon.classList.remove("metronome-stop-icon");
icon.classList.add("metronome-play-icon");
text.textContent = "Play";
button.classList.remove("running");
// Reset all beats and arrows
beats.forEach(beat => beat.style.color = "black");
downQuarterBeatButtons.forEach(btn => {
if (btn.classList.contains("metronome-selected-button")) {
btn.firstElementChild.className = "metronome-arrow-down-blue-icon";
} else {
btn.firstElementChild.className = "metronome-arrow-down-black-icon";
}
btn.classList.remove("metronome-active-btn");
});
upQuarterBeatButtons.forEach(btn => {
if (btn.classList.contains("metronome-selected-button")) {
btn.firstElementChild.className = "metronome-arrow-up-blue-icon";
} else {
btn.firstElementChild.className = "metronome-arrow-up-black-icon";
}
btn.classList.remove("metronome-active-btn");
});
currentBeatIndex = 0;
// Enable controls when stopping
timeSignature.removeAttribute("disabled");
bpmInput.removeAttribute("disabled");
upQuarterBeatButtons.forEach(btn => btn.removeAttribute("disabled"));
downQuarterBeatButtons.forEach(btn => btn.removeAttribute("disabled"));
} else {
const bpm = parseInt(bpmInput.value, 10);
let subBeat = 0; // Tracks which quarter note or half note we are one
let activeDownButton = document.getElementById('down-1.0');
let activeUpButton = document.getElementById('up-1.0');
if (activeDownButton.classList.contains("metronome-selected-button")) {
activeDownButton.firstElementChild.className = "metronome-arrow-down-white-icon";
activeDownButton.classList.add("metronome-active-btn");
downStrum.play();
}
if (activeUpButton.classList.contains("metronome-selected-button")) {
activeUpButton.firstElementChild.className = "metronome-arrow-up-white-icon";
activeUpButton.classList.add("metronome-active-btn");
upStrum.play();
}
// Highlight the first beat initially
beats[currentBeatIndex].style.color = "red";
//console.log("Tick - Beat", 1, "SubBeat:", "1.0"); // Debug log
let beatDivide = 4;
let subBeatMultiply = 25;
if (timeSelection === 68) {
beatDivide = 2;
subBeatMultiply = 50;
}
interval = setInterval(() => {
// Reset all arrows
downQuarterBeatButtons.forEach(btn => {
if (btn.classList.contains("metronome-selected-button")) {
btn.firstElementChild.className = "metronome-arrow-down-blue-icon";
} else {
btn.firstElementChild.className = "metronome-arrow-down-black-icon";
}
btn.classList.remove("metronome-active-btn");
});
upQuarterBeatButtons.forEach(btn => {
if (btn.classList.contains("metronome-selected-button")) {
btn.firstElementChild.className = "metronome-arrow-up-blue-icon";
} else {
btn.firstElementChild.className = "metronome-arrow-up-black-icon";
}
btn.classList.remove("metronome-active-btn");
});
subBeat = (subBeat + 1) % beatDivide;
if (subBeat === 0) { // If we've cycled through all 4 subBeats, move to the next beat
beats[currentBeatIndex].style.color = "black"; // Reset previous beat
currentBeatIndex = (currentBeatIndex + 1) % beats.length;
beats[currentBeatIndex].style.color = "red"; // Highlight the new beat
}
// Find the matching quarter or half beat button
const subBeatId = `${currentBeatIndex + 1}.${subBeat * subBeatMultiply}`;
activeDownButton = document.getElementById(`down-${subBeatId}`);
activeUpButton = document.getElementById(`up-${subBeatId}`);
if (activeDownButton.classList.contains("metronome-selected-button")) {
activeDownButton.firstElementChild.className = "metronome-arrow-down-white-icon";
activeDownButton.classList.add("metronome-active-btn");
downStrum.play();
}
if (activeUpButton.classList.contains("metronome-selected-button")) {
activeUpButton.firstElementChild.className = "metronome-arrow-up-white-icon";
activeUpButton.classList.add("metronome-active-btn");
upStrum.play();
}
//console.log("Tick - Beat", currentBeatIndex + 1, "subBeat:", subBeatId); // Debug log
}, (60 / bpm) * 1000 / beatDivide); // Divide by subBeats
icon.classList.remove("metronome-play-icon");
icon.classList.add("metronome-stop-icon");
text.textContent = "Stop";
button.classList.add("running");
// Disable controls when playing
timeSignature.setAttribute("disabled", true);
bpmInput.setAttribute("disabled", true);
upQuarterBeatButtons.forEach(btn => btn.setAttribute("disabled", true));
downQuarterBeatButtons.forEach(btn => btn.setAttribute("disabled", true));
}
}
button.addEventListener("click", playMetronome);
};
export default function createMetronome() {
document.addEventListener('touchstart', unlockAudio, false);
// Inject CSS into the document
if (!document.querySelector("#metronome-style")) {
const style = document.createElement("style");
style.id = "metronome-style";
style.textContent = metronomeCSS;
document.head.appendChild(style);
}
// Inject HTML into the page
const container = document.querySelector("#metronome-container");
container.innerHTML = metronomeContainerHTML;
const timeSignature = container.querySelector("#metronome-time-signature");
document.getElementById("metronome-time-signature").addEventListener("change", function () {
const selectedValue = this.value;
switch (selectedValue) {
case "44":
selectMetronome(44);
break;
case "34":
selectMetronome(34);
break;
case "68":
selectMetronome(68);
break;
}
});
selectMetronome(44);
}