Created
February 6, 2024 15:29
-
-
Save ahallora/2d1ca35523b3347d46537220a9676f7c to your computer and use it in GitHub Desktop.
Web Audio API react hook to easily stream music with fade in and out (working on Safari iOS too)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useState, useEffect } from "react"; | |
export const useAudioContext = () => { | |
const [audioContext, setAudioContext] = useState(null); | |
const [currentSourceNode, setCurrentSourceNode] = useState(null); | |
const [gainNode, setGainNode] = useState(null); | |
useEffect(() => { | |
const context = new AudioContext(); | |
setAudioContext(context); | |
return () => { | |
context.close(); | |
}; | |
}, []); | |
useEffect(() => { | |
if (audioContext) { | |
const gain = audioContext.createGain(); | |
gain.connect(audioContext.destination); | |
setGainNode(gain); | |
} | |
}, [audioContext]); | |
const playAudio = async (source, offset = 0) => { | |
if (audioContext && gainNode) { | |
try { | |
const response = await fetch(source); | |
const arrayBuffer = await response.arrayBuffer(); | |
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer); | |
// Create a new source node for the new audio | |
const newSourceNode = audioContext.createBufferSource(); | |
newSourceNode.buffer = audioBuffer; | |
newSourceNode.connect(gainNode); | |
// Fade out currently playing source if exists | |
if (currentSourceNode) { | |
const currentTime = audioContext.currentTime; | |
gainNode.gain.setValueAtTime(1, currentTime); | |
gainNode.gain.linearRampToValueAtTime(0, currentTime + 2); | |
setTimeout(() => { | |
currentSourceNode.stop(); | |
}, 2000); // Adjust based on fade-out duration | |
} | |
// Start playing the new source node with a fade-in effect | |
const startTime = audioContext.currentTime + 2; // Start after fade-out completes | |
newSourceNode.start(startTime, offset); | |
gainNode.gain.setValueAtTime(0, startTime); | |
gainNode.gain.linearRampToValueAtTime(1, startTime + 2); | |
// Update the current source node state | |
setCurrentSourceNode(newSourceNode); | |
} catch (error) { | |
console.error("Error playing audio:", error); | |
} | |
} | |
}; | |
const stopAudio = () => { | |
if (audioContext && currentSourceNode) { | |
const currentTime = audioContext.currentTime; | |
gainNode.gain.setValueAtTime(1, currentTime); | |
gainNode.gain.linearRampToValueAtTime(0, currentTime + 2); | |
setTimeout(() => { | |
currentSourceNode.stop(); | |
}, 2000); // Adjust based on fade-out duration | |
} | |
}; | |
return { | |
playAudio, | |
stopAudio, | |
}; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment