React Web Audio API - Play, pause and export loaded audio file
My purpose is to upload and listen to an audio file using the WEB Audio API. I have been able to listen to the audio file when selecting it, but having trouble pausing and playing it afterwards. I need to export the file to WAV format aswell. I have created a simple example, any help will be much appreciated
Loading Audio and playing it from file input
const onFileChange = (e) => {
let file = e.target.files[0];
console.log(file);
setFile(file);
let fileReader = new FileReader();
fileReader.onload = function (ev) {
audioContext.decodeAudioData(ev.target.result).then(function (buffer) {
playSound(buffer);
});
};
fileReader.readAsArrayBuffer(file);
};
Play sound using buffer source
const playSound = (buffer, time) => {
source = audioContext.createBufferSource();
source.buffer = buffer;
source.connect(audioContext.destination);
source.start(time);
setIsPlaying(true);
};
I'm facing problem here with pausing and playing:
const onPlayPause = (e) => {
console.log("audioState", audioContext.state);
console.log("duration", audioContext.currentTime);
if (!isPlaying) {
//source.start();
setIsPlaying(true);
} else if (audioContext.state === "running") {
setPlayDuration(audioContext.currentTime);
audioContext.suspend();
setIsPlaying(false);
} else if (audioContext.state === "suspended") {
audioContext.resume();
}
};
Export Audio:
const exportAudioFile = () => {
offlineContext.render().then((buffer) => {
setRenderState('encoding');
const handleMessage = ({ data }) => {
var blob = new window.Blob([new DataView(data)], {
type: 'audio/wav',
});
//blob = new Blob([buffer], { type: "audio/wav" });
const url = window.URL.createObjectURL(blob);
}
window.URL.revokeObjectURL(url);
})};
Codesandboxlink: https://codesandbox.io/s/react-audiocontext-pause-play-fw20u?file=/src/App.js
Solution 1:
I've had my fair share of headaches with persisting things in react functional components. Fortunately, useRef is an awesome tool for just that:
https://reactjs.org/docs/hooks-reference.html#useref
As the documentation says, it essentially returns a container which .current
property persists across re-renders.
I forked your code to useRef in action:
https://codesandbox.io/s/react-audiocontext-pause-play-forked-si59u?file=/src/App.js:120-159
Basically, when you load the file, store your shiny new AudioContext
in the ref's .current
field, and reference that throughout the rest of your component. You can clean it up a bit, IE store the .current
in a constant scoped to the function you're using it in.
Two key spots:
export default function App() {
const audioCtxContainer = useRef(null);
...
and
audioCtxContainer.current = new AudioContext();
audioCtxContainer.current
.decodeAudioData(ev.target.result)
.then(function (buffer) {
playSound(buffer);
});
useRef is useful for any mutable object that you want to persist for the lifetime of the component.
Let me know if that helps!