I'm trying to play around for the first time with the WebAudio API together with React.
My idea was to build a simple button that, once clicked, would start or stop a sound.
With the following code, I'm always getting the error "Failed to execute 'disconnect' on 'AudioNode': the given destination is not connected."
How can I fix it? Thanks!
import { useState } from 'react';
function App() {
const [ dataPlaying, setDataPlaying ] = useState(false)
const AudioContext = window.AudioContext || window.webkitAudioContext
const audioContext = new AudioContext()
let osc = audioContext.createOscillator()
osc.type = 'sine'
osc.frequency.value = 880
const createOscillator = () => {
if (dataPlaying === false) {
osc.start()
osc.connect(audioContext.destination)
setDataPlaying(true)
} else {
osc.disconnect(audioContext.destination)
setDataPlaying(false)
}
}
return (
<div className="App">
<button
onClick={() => createOscillator() }
data-playing={ dataPlaying }>
<span>Play/Pause</span>
</button>
</div>
);
}
export default App;
Here's my attempt to resolve the connection error.
AudioContext
external to the component.useRef
hook to store an audio context to persist through rerenders.useEffect
hook to instantiate an oscillator and manage audo context connection.Updated Code
import React, { useEffect, useRef, useState } from "react";
const AudioContext = window.AudioContext || window.webkitAudioContext;
export default function App() {
const [dataPlaying, setDataPlaying] = useState(false);
const audioContextRef = useRef();
useEffect(() => {
const audioContext = new AudioContext();
const osc = audioContext.createOscillator();
osc.type = "sine";
osc.frequency.value = 880;
// Connect and start
osc.connect(audioContext.destination);
osc.start();
// Store context and start suspended
audioContextRef.current = audioContext;
audioContext.suspend();
// Effect cleanup function to disconnect
return () => osc.disconnect(audioContext.destination);
}, []);
const toggleOscillator = () => {
if (dataPlaying) {
audioContextRef.current.suspend();
} else {
audioContextRef.current.resume();
}
setDataPlaying((play) => !play);
};
return (
<div className="App">
<button onClick={toggleOscillator} data-playing={dataPlaying}>
<span>{dataPlaying ? "Pause" : "Play"}</span>
</button>
</div>
);
}