Reputation: 65
Newbie here, I'm trying to make a Javascript metronome, but I'm stuck on making the sound play according to the current bpm of the project
Here's where I think the problem lies, when I press play, it sounds, but in a fixed bpm, that is not the one being inputted in the bpm variable:
//VARIABLES
let bpm = 150;
let soundInterval = (60/bpm)*1000;
//FUNCTIONS
//START METRONOME
let startStopMetronome = startButton.addEventListener('click', () => {
startMetronome(soundInterval);
})
function startMetronome(si) {
setTimeout(() => {
primaryBeat.play();
startMetronome(si);
},si);
}
UPDATE:
I´ve tried making the soundInterval
update with a function, but it still does the same, and plays the same fixed interval no matter the bpm changes
//VARIABLES
let bpm = 150;
let soundInterval;
//FUNCTIONS
//START METRONOME
let startStopMetronome = startButton.addEventListener('click', () => {
soundInterval = calculateSoundInterval();
startMetronome(soundInterval);
})
function startMetronome(si) {
setTimeout(() => {
primaryBeat.play();
startMetronome(si);
},si);
}
let calculateSoundInterval = () => {
return (60/bpm)*1000;
}
let updateBpmInDisplay = display.addEventListener('change', ()=> {
soundInterval = calculateSoundInterval();
})
Upvotes: 1
Views: 1076
Reputation: 65
Thanks everyone for the help. At the end, the issue was that I had not configured the inputs to reset the sound on click or input, so the sound just played until it stopped naturally. I added currentTime = 0, and problem solved, now it works as intended:
function startMetronome(si) {
timerId = setInterval(() => {
primaryBeat.play();
primaryBeat.currentTime = 0;
},si);
}
Upvotes: 0
Reputation: 2395
I thought it might be fun to look over how I would accomplish this. I used an AudioContext and oscillator to make the sound, and instead of using setTimeout directly I used a helper function to model async practice. If I wanted to improve upon this, I might hold onto my timeout in a higher scope, so when stop is pressed I could clear the timeout and prevent any bugs when pressing stop and start quickly to have two loops running at the same time. I hope my take on things has some value 👍 was fun to look at.
const get = str => document.querySelector(str);
const wait = seconds => new Promise(r => setTimeout(r, seconds * 1e3));
const context = new AudioContext();
let cntr = 0;
const makeSound = () => {
const sound = context.createOscillator();
const fourthBeat = cntr++ % 4 === 0;
sound.frequency.value = fourthBeat ? 400 : 440;
sound.connect(context.destination);
sound.start(context.currentTime);
sound.stop(context.currentTime + .1);
};
let bpm = 60;
get("input").addEventListener("input", e => {
bpm = e.target.value;
get(".display").innerText = bpm;
});
let running = true;
get(".start").addEventListener("click", async () => {
running = true;
get(".start").disabled = true;
while (running) {
makeSound();
await wait(60 / bpm);
}
});
get(".stop").addEventListener("click", () => {
running = false
get(".start").disabled = false;
cntr = 0;
});
<div class="display">60</div>
<input min="30" max="200" value="60" type="range" />
<button class="start">start</button>
<button class="stop">stop</button>
Upvotes: 1