I'm building a Text-to-Speech (TTS) component in React, where I want to specify the audio output device using sinkId (e.g., a selected speaker). Despite setting sinkId on the audio element, the audio consistently plays through the default output device instead of the selected one.
In this setup, I'm using SpeechSynthesisUtterance to generate TTS playback, and I tried routing the audio through an AudioContext with a MediaStreamDestination to control the output. However, sinkId does not seem to take effect. Any suggestions for correctly setting the output device?
example code :
import { useEffect, useRef, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
const UseTTS = () => {
const [isSpeaking, setSpeaking] = useState(false);
const [isPaused, setIsPaused] = useState(false);
const audioContextRef = useRef(null);
const audioElement = useRef(null);
const utteranceRef = useRef(null);
const { selectedSpeakerSrc } = useSelector(state => state.setup);
useEffect(() => {
// Initialize AudioContext and audio element
if (!audioContextRef.current) {
audioContextRef.current = new (window.AudioContext || window.webkitAudioContext)();
}
if (!audioElement.current) {
audioElement.current = new Audio();
}
}, []);
const handlePlay = async () => {
const ttsText = "Hello, this is a test message!";
if (!utteranceRef.current) {
utteranceRef.current = new SpeechSynthesisUtterance(ttsText);
utteranceRef.current.lang = 'en-US';
utteranceRef.current.onend = handleSpeechEnd;
}
try {
if (audioElement.current.setSinkId && selectedSpeakerSrc) {
await audioElement.current.setSinkId(selectedSpeakerSrc);
}
} catch (error) {
console.error(`Failed to set sinkId: ${error}`);
}
utteranceRef.current.voice = speechSynthesis.getVoices()[0];
speechSynthesis.speak(utteranceRef.current);
setSpeaking(true);
};
const handleSpeechEnd = () => {
setSpeaking(false);
utteranceRef.current = null;
};
return {
isSpeaking,
handlePlay,
};
};
export default UseTTS;