forked from open-webui/open-webui
		
	feat: tts optimisation
This commit is contained in:
		
							parent
							
								
									68ed24b7d7
								
							
						
					
					
						commit
						401799c6fa
					
				
					 2 changed files with 69 additions and 26 deletions
				
			
		| 
						 | 
				
			
			@ -15,6 +15,7 @@
 | 
			
		|||
	import CodeBlock from './CodeBlock.svelte';
 | 
			
		||||
 | 
			
		||||
	import { synthesizeOpenAISpeech } from '$lib/apis/openai';
 | 
			
		||||
	import { extractSentences } from '$lib/utils';
 | 
			
		||||
 | 
			
		||||
	export let modelfiles = [];
 | 
			
		||||
	export let message;
 | 
			
		||||
| 
						 | 
				
			
			@ -35,8 +36,10 @@
 | 
			
		|||
 | 
			
		||||
	let tooltipInstance = null;
 | 
			
		||||
 | 
			
		||||
	let audioMap = {};
 | 
			
		||||
	let sentencesAudio = {};
 | 
			
		||||
	let speaking = null;
 | 
			
		||||
	let speakingIdx = null;
 | 
			
		||||
 | 
			
		||||
	let loadingSpeech = false;
 | 
			
		||||
 | 
			
		||||
	$: tokens = marked.lexer(message.content);
 | 
			
		||||
| 
						 | 
				
			
			@ -116,44 +119,67 @@
 | 
			
		|||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	const playAudio = (idx) => {
 | 
			
		||||
		return new Promise((res) => {
 | 
			
		||||
			speakingIdx = idx;
 | 
			
		||||
			const audio = sentencesAudio[idx];
 | 
			
		||||
			audio.play();
 | 
			
		||||
			audio.onended = async (e) => {
 | 
			
		||||
				await new Promise((r) => setTimeout(r, 500));
 | 
			
		||||
 | 
			
		||||
				if (Object.keys(sentencesAudio).length - 1 === idx) {
 | 
			
		||||
					speaking = null;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				res(e);
 | 
			
		||||
			};
 | 
			
		||||
		});
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	const toggleSpeakMessage = async () => {
 | 
			
		||||
		if (speaking) {
 | 
			
		||||
			speechSynthesis.cancel();
 | 
			
		||||
			speaking = null;
 | 
			
		||||
 | 
			
		||||
			audioMap[message.id].pause();
 | 
			
		||||
			audioMap[message.id].currentTime = 0;
 | 
			
		||||
			sentencesAudio[speakingIdx].pause();
 | 
			
		||||
			sentencesAudio[speakingIdx].currentTime = 0;
 | 
			
		||||
 | 
			
		||||
			speaking = null;
 | 
			
		||||
			speakingIdx = null;
 | 
			
		||||
		} else {
 | 
			
		||||
			speaking = true;
 | 
			
		||||
 | 
			
		||||
			if ($settings?.speech?.engine === 'openai') {
 | 
			
		||||
				loadingSpeech = true;
 | 
			
		||||
				const res = await synthesizeOpenAISpeech(
 | 
			
		||||
					localStorage.token,
 | 
			
		||||
					$settings?.speech?.speaker,
 | 
			
		||||
					message.content
 | 
			
		||||
				).catch((error) => {
 | 
			
		||||
					toast.error(error);
 | 
			
		||||
					return null;
 | 
			
		||||
				});
 | 
			
		||||
 | 
			
		||||
				if (res) {
 | 
			
		||||
					const blob = await res.blob();
 | 
			
		||||
					const blobUrl = URL.createObjectURL(blob);
 | 
			
		||||
					console.log(blobUrl);
 | 
			
		||||
				const sentences = extractSentences(message.content);
 | 
			
		||||
				console.log(sentences);
 | 
			
		||||
 | 
			
		||||
					loadingSpeech = false;
 | 
			
		||||
				sentencesAudio = sentences.reduce((a, e, i, arr) => {
 | 
			
		||||
					a[i] = null;
 | 
			
		||||
					return a;
 | 
			
		||||
				}, {});
 | 
			
		||||
 | 
			
		||||
					const audio = new Audio(blobUrl);
 | 
			
		||||
					audioMap[message.id] = audio;
 | 
			
		||||
				let lastPlayedAudioPromise = Promise.resolve(); // Initialize a promise that resolves immediately
 | 
			
		||||
 | 
			
		||||
					audio.onended = () => {
 | 
			
		||||
						speaking = null;
 | 
			
		||||
						if ($settings.conversationMode) {
 | 
			
		||||
							document.getElementById('voice-input-button')?.click();
 | 
			
		||||
						}
 | 
			
		||||
					};
 | 
			
		||||
					audio.play().catch((e) => console.error('Error playing audio:', e));
 | 
			
		||||
				for (const [idx, sentence] of sentences.entries()) {
 | 
			
		||||
					const res = await synthesizeOpenAISpeech(
 | 
			
		||||
						localStorage.token,
 | 
			
		||||
						$settings?.speech?.speaker,
 | 
			
		||||
						sentence
 | 
			
		||||
					).catch((error) => {
 | 
			
		||||
						toast.error(error);
 | 
			
		||||
						return null;
 | 
			
		||||
					});
 | 
			
		||||
 | 
			
		||||
					if (res) {
 | 
			
		||||
						const blob = await res.blob();
 | 
			
		||||
						const blobUrl = URL.createObjectURL(blob);
 | 
			
		||||
						const audio = new Audio(blobUrl);
 | 
			
		||||
						sentencesAudio[idx] = audio;
 | 
			
		||||
						loadingSpeech = false;
 | 
			
		||||
 | 
			
		||||
						lastPlayedAudioPromise = lastPlayedAudioPromise.then(() => playAudio(idx));
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				let voices = [];
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue