I'm currently working on coding a digital audio workstation however I'm encountering an issue with my elements. I'm trying to implement a function in which if on a row, a dropzone is playing an audio, clicking on another dropzone of that same row would simultaneously stop playing the other dropzone and start playing the one I just clicked on. However what I am encountering is that I have to click two times on a dropzone for it to play (one time to stop/pause the other dropzone and one time to start playing the dropzone) which I do not get why Please find my code below and thank you so much in advance for all the answers and help!
const dropzones = document.getElementsByClassName("loop-cell");
const rowPlayingAudio = {};
Array.from(dropzones).forEach((dropzone, index) => {
let audio = null;
let isPlaying = false;
dropzone.addEventListener("dragover", (e) => {
e.preventDefault();
dropzone.style.borderColor = "blue";
});
dropzone.addEventListener("dragleave", () => {
if (!audio) {
dropzone.style.borderColor = "#fda0d3";
} else {
dropzone.style.borderColor = "#a2d0f1";
}
});
dropzone.addEventListener("drop", (e) => {
e.preventDefault();
dropzone.style.borderColor = "#a2d0f1";
const files = Array.from(e.dataTransfer.files).filter((file) =>
file.type.includes("audio/")
);
files.forEach((file) => {
const reader = new FileReader();
reader.onload = function(e) {
const audioSrc = e.target.result;
audio = new Audio(audioSrc);
audio.loop = true;
isPlaying = false;
};
reader.readAsDataURL(file);
});
});
dropzone.addEventListener("click", () => {
if (audio) {
const rowIndex = Math.floor(index / 8);
if (
rowPlayingAudio[rowIndex] &&
rowPlayingAudio[rowIndex].audio !== audio
) {
rowPlayingAudio[rowIndex].audio.pause();
rowPlayingAudio[rowIndex].audio.currentTime = 0;
rowPlayingAudio[rowIndex].isPlaying = false;
rowPlayingAudio[rowIndex] = null;
}
if (isPlaying) {
audio.pause();
audio.currentTime = 0;
isPlaying = false;
rowPlayingAudio[rowIndex] = null;
} else {
audio.play();
isPlaying = true;
rowPlayingAudio[rowIndex] = {
audio,
dropzone,
isPlaying,
index
};
}
audio.onended = () => {
setLaunchpadLight(index, 0);
isPlaying = false;
rowPlayingAudio[rowIndex] = null;
};
}
});
});
.loop-grid {
display: grid;
grid-template-columns: repeat(8, 1fr);
gap: 7px;
}
.loop-cell {
background: #fec9e6;
padding: 10px;
text-align: center;
border: 4px solid #fda0d3;
cursor: pointer;
transition: background-color 0.1s;
}
<!DOCTYPE html>
<html>
<body>
<h1>LaunchPad 2 Looper</h1>
<main>
<div class="loop-grid">
<div id="dropzone0" class="loop-cell">Keys</div>
<div id="dropzone1" class="loop-cell">Keys</div>
<div id="dropzone2" class="loop-cell">Keys</div>
<div id="dropzone3" class="loop-cell">Keys</div>
<div id="dropzone4" class="loop-cell">Keys</div>
<div id="dropzone5" class="loop-cell">Keys</div>
<div id="dropzone6" class="loop-cell">Keys</div>
<div id="dropzone7" class="loop-cell">Keys</div>
<div id="dropzone8" class="loop-cell">Bass</div>
<div id="dropzone9" class="loop-cell">Bass</div>
<div id="dropzone10" class="loop-cell">Bass</div>
<div id="dropzone11" class="loop-cell">Bass</div>
<div id="dropzone12" class="loop-cell">Bass</div>
<div id="dropzone13" class="loop-cell">Bass</div>
<div id="dropzone14" class="loop-cell">Bass</div>
<div id="dropzone15" class="loop-cell">Bass</div>
</div>
</main>
</body>
</html>
I tried deleting any potential duplicates but I don't get where the issue could be coming from
Great Minimal Reproducible Example.
I fixed the issue. Changes made:
rowPlayingAudio
) using the built in events play
, ended
, pause
. I am even toggling a class playing
on the playing element.audio
click
handler is much simpler, knowing status will be handled using the events.const dropzones = document.querySelectorAll(".loop-cell");
const rowPlayingAudio = {};
function setLaunchpadLight() {
// dummy
}
Array.from(dropzones).forEach((dropzone, index) => {
let audio = null;
let isPlaying = false;
const rowIndex = Math.floor(index / 8);
dropzone.addEventListener("dragover", (e) => {
e.preventDefault();
dropzone.style.borderColor = "blue";
});
dropzone.addEventListener("dragleave", () => {
if (!audio) {
dropzone.style.borderColor = "#fda0d3";
} else {
dropzone.style.borderColor = "#a2d0f1";
}
});
dropzone.addEventListener("drop", (e) => {
e.preventDefault();
dropzone.style.borderColor = "#a2d0f1";
const files = Array.from(e.dataTransfer.files).filter((file) =>
file.type.includes("audio/")
);
files.forEach((file) => {
const reader = new FileReader();
reader.onload = function(e) {
const audioSrc = e.target.result;
audio = new Audio(audioSrc);
audio.loop = true;
isPlaying = false;
function onStopForAnyReason(ev) {
dropzone.classList.remove("playing")
setLaunchpadLight(index, 0);
isPlaying = false;
audio.currentTime = 0;
delete rowPlayingAudio[rowIndex];
}
audio.onended = onStopForAnyReason
audio.onpause = onStopForAnyReason
audio.onplay = () => {
dropzone.classList.add("playing")
isPlaying = true;
rowPlayingAudio[rowIndex] = {
audio,
dropzone,
isPlaying,
index
};
}
};
reader.readAsDataURL(file);
});
});
dropzone.addEventListener("click", () => {
if (audio) {
if (rowPlayingAudio[rowIndex]) {
rowPlayingAudio[rowIndex].audio.pause();
}
if (isPlaying) {
audio.pause();
} else {
audio.play();
}
isPlaying = !isPlaying
}
});
});
.loop-grid {
display: grid;
grid-template-columns: repeat(8, 1fr);
gap: 7px;
}
.loop-cell {
background: #fec9e6;
padding: 10px;
text-align: center;
border: 4px solid #fda0d3;
cursor: pointer;
transition: background-color 0.1s;
}
.loop-cell.playing {
background: green;
}
<h1>LaunchPad 2 Looper</h1>
<main>
<div class="loop-grid">
<div id="dropzone0" class="loop-cell">Keys</div>
<div id="dropzone1" class="loop-cell">Keys</div>
<div id="dropzone2" class="loop-cell">Keys</div>
<div id="dropzone3" class="loop-cell">Keys</div>
<div id="dropzone4" class="loop-cell">Keys</div>
<div id="dropzone5" class="loop-cell">Keys</div>
<div id="dropzone6" class="loop-cell">Keys</div>
<div id="dropzone7" class="loop-cell">Keys</div>
<div id="dropzone8" class="loop-cell">Bass</div>
<div id="dropzone9" class="loop-cell">Bass</div>
<div id="dropzone10" class="loop-cell">Bass</div>
<div id="dropzone11" class="loop-cell">Bass</div>
<div id="dropzone12" class="loop-cell">Bass</div>
<div id="dropzone13" class="loop-cell">Bass</div>
<div id="dropzone14" class="loop-cell">Bass</div>
<div id="dropzone15" class="loop-cell">Bass</div>
</div>
</main>