Skip to content

Instantly share code, notes, and snippets.

@ahallora
Created February 6, 2024 15:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ahallora/2d1ca35523b3347d46537220a9676f7c to your computer and use it in GitHub Desktop.
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)
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