Detect browser audio codec support, use opus for Firefox
Firefox on Linux lacks mp3 decoders. Detect browser capabilities at load time: use mp3 for Chrome, opus (ogg) for Firefox. MediaSource streaming only works with mp3 (Chrome); Firefox falls back to buffered playback with opus format. Sends response_format parameter to TTS server.
This commit is contained in:
parent
3019fd0e15
commit
74a1ea581c
1 changed files with 35 additions and 5 deletions
|
|
@ -19,6 +19,24 @@ export function setTTSMode(mode) {
|
|||
}
|
||||
}
|
||||
|
||||
// Detect best audio format for this browser
|
||||
// Chrome: mp3 works everywhere, MediaSource supports audio/mpeg
|
||||
// Firefox: mp3 may lack decoders on Linux; opus (ogg) always works
|
||||
// For MediaSource streaming: only Chrome supports audio/mpeg, nobody supports audio/ogg in MSE
|
||||
function detectAudioFormat() {
|
||||
const audio = document.createElement('audio');
|
||||
// Check if the browser can play mp3 at all
|
||||
const canMp3 = audio.canPlayType('audio/mpeg') === 'probably' || audio.canPlayType('audio/mpeg') === 'maybe';
|
||||
if (canMp3) {
|
||||
return { format: 'mp3', mime: 'audio/mpeg', mseMime: 'audio/mpeg' };
|
||||
}
|
||||
// Fallback to opus (ogg container) - Firefox always supports this
|
||||
console.log("Browser lacks mp3 support, using opus format");
|
||||
return { format: 'opus', mime: 'audio/ogg', mseMime: null }; // MSE doesn't support ogg
|
||||
}
|
||||
|
||||
const AUDIO_FORMAT = detectAudioFormat();
|
||||
|
||||
// Simple JavaScript text cleaning for TTS
|
||||
function cleanTextForTTS(text) {
|
||||
return text
|
||||
|
|
@ -55,6 +73,7 @@ export async function speakText(text, voice = "alloy", rate = 0.9, model = "tts-
|
|||
model: model,
|
||||
voice: voice,
|
||||
input: spokenText,
|
||||
response_format: AUDIO_FORMAT.format,
|
||||
}),
|
||||
});
|
||||
|
||||
|
|
@ -97,6 +116,7 @@ export async function speakTextDirect(text, voice = "alloy", rate = 0.9, model =
|
|||
model: model,
|
||||
voice: voice,
|
||||
input: cleanedText,
|
||||
response_format: AUDIO_FORMAT.format,
|
||||
}),
|
||||
});
|
||||
|
||||
|
|
@ -128,6 +148,15 @@ export async function speakTextStreaming(text, voice = "alloy", rate = 0.9, mode
|
|||
const cleanedText = cleanTextForTTS(text);
|
||||
console.log("TTS streaming input preview:", cleanedText.substring(0, 100) + "...");
|
||||
|
||||
// Determine if we can use MediaSource for true streaming
|
||||
const mseMime = AUDIO_FORMAT.mseMime;
|
||||
const canStream = mseMime && window.MediaSource && MediaSource.isTypeSupported(mseMime);
|
||||
// For streaming via MSE, request mp3 (only format MSE supports well)
|
||||
// For buffered fallback, use detected format (opus for Firefox)
|
||||
const requestFormat = canStream ? 'mp3' : AUDIO_FORMAT.format;
|
||||
|
||||
console.log(`TTS streaming: canStream=${canStream}, format=${requestFormat}, mseMime=${mseMime}`);
|
||||
|
||||
const response = await fetch(TTS_API_URL, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
|
|
@ -138,6 +167,7 @@ export async function speakTextStreaming(text, voice = "alloy", rate = 0.9, mode
|
|||
model: model,
|
||||
voice: voice,
|
||||
input: cleanedText,
|
||||
response_format: requestFormat,
|
||||
}),
|
||||
});
|
||||
|
||||
|
|
@ -145,9 +175,9 @@ export async function speakTextStreaming(text, voice = "alloy", rate = 0.9, mode
|
|||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
// Check if MediaSource is supported and can handle audio/mpeg
|
||||
if (!window.MediaSource || !MediaSource.isTypeSupported('audio/mpeg')) {
|
||||
console.warn("MediaSource API doesn't support audio/mpeg, falling back to buffered mode");
|
||||
// If MSE isn't available for this format, fall back to buffered playback
|
||||
if (!canStream) {
|
||||
console.warn(`MediaSource can't stream ${requestFormat}, using buffered playback with ${AUDIO_FORMAT.format}`);
|
||||
const audioBlob = await response.blob();
|
||||
const audioUrl = URL.createObjectURL(audioBlob);
|
||||
const audio = new Audio(audioUrl);
|
||||
|
|
@ -169,7 +199,7 @@ export async function speakTextStreaming(text, voice = "alloy", rate = 0.9, mode
|
|||
mediaSource.addEventListener('sourceopen', async () => {
|
||||
let sourceBuffer;
|
||||
try {
|
||||
sourceBuffer = mediaSource.addSourceBuffer('audio/mpeg');
|
||||
sourceBuffer = mediaSource.addSourceBuffer(mseMime);
|
||||
} catch (e) {
|
||||
console.error("Failed to create SourceBuffer:", e);
|
||||
reject(e);
|
||||
|
|
@ -237,7 +267,7 @@ export async function speakTextStreaming(text, voice = "alloy", rate = 0.9, mode
|
|||
console.log(`TTS streaming complete: ${totalBytes} bytes`);
|
||||
|
||||
// Build blob for download/storage
|
||||
const audioBlob = new Blob(chunks, { type: 'audio/mpeg' });
|
||||
const audioBlob = new Blob(chunks, { type: mseMime });
|
||||
setLastTTS(text, { audio, blob: audioBlob });
|
||||
resolve({ blob: audioBlob, blobUrl: audioUrl });
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue