I have a React Next web app that plays songs using web audio api. I noticed that after browsing a lot of songs, at some point my app won't load new songs.
In Chrome I get this error: Failed to execute 'decodeAudioData' on 'BaseAudioContext': Unable to decode audio data, and only after refreshing the page (sometimes need to refresh 2-3 times), I can load songs again. Looking at "Total JS heap size" in the Memory tab in Chrome devtools doesn't indicate a memory leak.
In Safari it just refreshes the page automatically.
To make sure it's not something in my code, I made a new web app with a minimal example, and I could reproduce the issue. I get the decoding error after ~250 times.
export const test = async () => {
const audioContext = new AudioContext();
const blob = (
await axios.get("./url-to-some-song.wav", {
responseType: "blob",
})
).data;
for (let i = 0; i < 300; i++) {
const arrayBuffer = await blob.arrayBuffer();
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
}
};
I tried to add a delay after every iteration - it doesn't help. It looks like browsers may have some limits, but I couldn't find any documentation for that. What could it be?
I can reproduce the problem in Chrome v122. It seems to be fixed when using structuredClone()
. But I don't have a good explanation why that works. I guess it's a bug in Chrome.
export const test = async () => {
const audioContext = new AudioContext();
const blob = (
await axios.get("./url-to-some-song.wav", {
responseType: "blob",
})
).data;
for (let i = 0; i < 300; i++) {
const arrayBuffer = await blob.arrayBuffer();
const audioBuffer = await audioContext.decodeAudioData(
structuredClone(arrayBuffer)
);
}
};
You could also avoid the additional step by getting the data as 'arrayBuffer'
directly.
export const test = async () => {
const audioContext = new AudioContext();
const arrayBuffer = (
await axios.get("./url-to-some-song.wav", {
responseType: "arrayBuffer",
})
).data;
for (let i = 0; i < 300; i++) {
const audioBuffer = await audioContext.decodeAudioData(
structuredClone(arrayBuffer)
);
}
};