Need help with detecting loudness with wavesurfer lib (React) #3807
-
Here I have sample code to check loudness detection when playing audio (from 0-1) (this code works perfectly) <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Loudness Detection</title>
</head>
<body>
<input type="file" id="audioFile" accept="audio/mp3" />
<p>Loudness: <span id="loudnessValue">0</span></p>
<script>
document.getElementById("audioFile").addEventListener("change", function () {
const fileInput = document.getElementById("audioFile");
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const analyser = audioContext.createAnalyser();
analyser.fftSize = 256;
const dataArray = new Uint8Array(analyser.frequencyBinCount);
if (fileInput.files.length === 0) {
alert("Please select an MP3 file.");
return;
}
const file = fileInput.files[0];
const reader = new FileReader();
reader.onload = function (event) {
audioContext.decodeAudioData(event.target.result, function (buffer) {
const source = audioContext.createBufferSource();
console.log(buffer)
source.buffer = buffer;
source.connect(analyser);
analyser.connect(audioContext.destination);
source.start(0);
function updateLoudness() {
analyser.getByteFrequencyData(dataArray);
let sum = 0;
for (let i = 0; i < dataArray.length; i++) {
sum += dataArray[i];
}
const average = sum / dataArray.length;
const loudness = average / 256;
document.getElementById("loudnessValue").textContent = loudness.toFixed(2);
requestAnimationFrame(updateLoudness);
}
updateLoudness();
});
};
reader.readAsArrayBuffer(file);
});
</script>
</body>
</html> The same thing is done in React: const [volumeVal, setVolumeVal] = useState(0);
const [analyzer, setAnalyzer] = useState(null);
const [dataArray, setDataArray] = useState(null);
const timeUpdate = useCallback(() => {
if (!analyzer || !dataArray) return;
analyzer.getByteFrequencyData(dataArray);
let sum = 0;
for (let i = 0; i < dataArray.length; i++) {
sum += dataArray[i];
}
const average = sum / dataArray.length;
const loudness = average / 256;
console.log(`Loudness: ${loudness}`); //from 0 to 1
setVolumeVal(loudness);
}, [karaokeSong]);
useEffect(() => {
if (!karaokeSong) return;
const ws = karaokeSong.getWs();
if (!ws) return;
const mediaElement = ws.getMediaElement();
const audioContext = mediaElement.audioContext
const analyser = audioContext.createAnalyser();
analyser.fftSize = 256;
const dataArr = new Uint8Array(analyser.frequencyBinCount);
const source = audioContext.createBufferSource();
source.buffer = mediaElement.buffer;
source.connect(analyser);
analyser.connect(audioContext.destination);
setDataArray(dataArr);
setAnalyzer(analyser);
ws.on("timeupdate", timeUpdate);
return () => {
if (ws) {
ws.un("timeupdate", timeUpdate);
}
};
}, [karaokeSong, karaokeSong?.getWs(), timeUpdate]); Except this time the loudness always is detected as 0 when playing. Any clue on how to fix it with the lib? |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 5 replies
-
Also do note that the wavesurfer source initialization is from using this: const webAudioPlayer = new WebAudioPlayer();
webAudioPlayer.src = assetUrl;
this.loadMetadataFunc = () => {
this.ws = WaveSurfer.create({
container: this.waveformDom,
waveColor: "#4F4A85",
progressColor: "#4F4A85",
cursorColor: "#FFF",
media: webAudioPlayer,
peaks: webAudioPlayer.getChannelData(),
duration: webAudioPlayer.duration,
height: 80,
backend: "MediaElement",
});
};
webAudioPlayer.addEventListener("loadedmetadata", this.loadMetadataFunc); |
Beta Was this translation helpful? Give feedback.
-
Try debugging it. Check how many times your effects run and with what values in the variables. From what I can spot, the problem might be that you're creating another buffer source with the same buffer.
Try replacing this with: const source = ws.getMediaElement().getGainNode()
source.disconnect()
source.connect(analyser)
analyser.connect(audioContext.destination) |
Beta Was this translation helpful? Give feedback.
-
Woo nice, thank you so much. const [volumeVal, setVolumeVal] = useState(0);
const analyzerRef = useRef(null);
const dataArrayRef = useRef(null);
const timeUpdate = useCallback(() => {
if (!analyzerRef.current || !dataArrayRef.current) return;
analyzerRef.current.getByteFrequencyData(dataArrayRef.current);
let sum = 0;
for (let i = 0; i < dataArrayRef.current.length; i++) {
sum += dataArrayRef.current[i];
}
const average = sum / dataArrayRef.current.length;
const loudness = average / 256;
setVolumeVal(loudness);
}, []);
useEffect(() => {
if (!karaokeSong) return;
const ws = karaokeSong.getWs();
if (!ws) return;
const mediaElement = ws.getMediaElement();
const audioContext = mediaElement.audioContext;
const analyser = audioContext.createAnalyser();
analyser.fftSize = 256;
const dataArr = new Uint8Array(analyser.frequencyBinCount);
const source = ws.getMediaElement().getGainNode();
source.disconnect();
source.connect(analyser);
analyser.connect(audioContext.destination);
dataArrayRef.current = dataArr;
analyzerRef.current = analyser;
ws.on("timeupdate", timeUpdate);
return () => {
if (ws) {
ws.un("timeupdate", timeUpdate);
}
};
}, [karaokeSong, karaokeSong?.getWs(), timeUpdate]); |
Beta Was this translation helpful? Give feedback.
-
Here is the updated code so far: const [volumeVal1, setVolumeVal1] = useState(0);
const [volumeVal2, setVolumeVal2] = useState(0);
const peaks = useRef([[], []]);
const timeUpdate = useCallback(() => {
if (!karaokeSong) return;
let ws = karaokeSong.getWs();
if (!ws || !peaks.current[0] || !peaks.current[1]) return;
//If it is not playing, set loudness to 0
if (!ws.isPlaying()) {
setVolumeVal1(0);
setVolumeVal2(0);
return;
}
let currentTime = ws.getCurrentTime();
let duration = ws.getDuration();
let val1 =
peaks.current[0][
Math.floor((currentTime / duration) * peaks.current[0].length)
];
let val2 =
peaks.current[1][
Math.floor((currentTime / duration) * peaks.current[1].length)
];
if (val1 > 0) setVolumeVal1(val1);
if (val2 > 0) setVolumeVal2(val2);
}, [karaokeSong?.getWs()]);
useEffect(() => {
if (!karaokeSong) return;
const ws = karaokeSong.getWs();
if (!ws) return;
peaks.current = ws.exportPeaks();
ws.on("timeupdate", timeUpdate);
return () => {
if (ws) {
ws.un("timeupdate", timeUpdate);
}
};
}, [karaokeSong, karaokeSong?.getWs(), timeUpdate]);
Am I doing this right? Although the movement kinda looks snappy, but it still works. |
Beta Was this translation helpful? Give feedback.
Try debugging it. Check how many times your effects run and with what values in the variables.
From what I can spot, the problem might be that you're creating another buffer source with the same buffer.
Try replacing this with: