ソースを参照

Merge pull request #5509 from open-webui/dev-playback-controls

feat: playback controls
Timothy Jaeryang Baek 8 ヶ月 前
コミット
9fc6b999d0

+ 7 - 7
src/lib/components/chat/MessageInput/CallOverlay.svelte

@@ -1,15 +1,13 @@
 <script lang="ts">
 	import { config, models, settings, showCallOverlay } from '$lib/stores';
 	import { onMount, tick, getContext, onDestroy, createEventDispatcher } from 'svelte';
+	import { DropdownMenu } from 'bits-ui';
+	import Dropdown from '$lib/components/common/Dropdown.svelte';
+	import { flyAndScale } from '$lib/utils/transitions';
 
 	const dispatch = createEventDispatcher();
 
-	import {
-		blobToFile,
-		calculateSHA256,
-		extractSentencesForAudio,
-		findWordIndices
-	} from '$lib/utils';
+	import { blobToFile } from '$lib/utils';
 	import { generateEmoji } from '$lib/apis';
 	import { synthesizeOpenAISpeech, transcribeAudio } from '$lib/apis/audio';
 
@@ -360,6 +358,7 @@
 								?.at(0) ?? undefined;
 
 						currentUtterance = new SpeechSynthesisUtterance(content);
+						currentUtterance.rate = $settings.audio?.tts?.speedRate ?? 1;
 
 						if (voice) {
 							currentUtterance.voice = voice;
@@ -381,11 +380,12 @@
 	const playAudio = (audio) => {
 		if ($showCallOverlay) {
 			return new Promise((resolve) => {
-				const audioElement = document.getElementById('audioElement');
+				const audioElement = document.getElementById('audioElement') as HTMLAudioElement;
 
 				if (audioElement) {
 					audioElement.src = audio.src;
 					audioElement.muted = true;
+					audioElement.playbackRate = $settings.audio?.tts?.speedRate ?? 1;
 
 					audioElement
 						.play()

+ 5 - 2
src/lib/components/chat/Messages/ResponseMessage.svelte

@@ -204,6 +204,8 @@
 					const blob = await res.blob();
 					const blobUrl = URL.createObjectURL(blob);
 					const audio = new Audio(blobUrl);
+					audio.playbackRate = $settings.audio?.tts?.speedRate ?? 1;
+
 					audioParts[idx] = audio;
 					loadingSpeech = false;
 					lastPlayedAudioPromise = lastPlayedAudioPromise.then(() => playAudio(idx));
@@ -226,6 +228,7 @@
 					console.log(voice);
 
 					const speak = new SpeechSynthesisUtterance(message.content);
+					speak.rate = $settings.audio?.tts?.speedRate ?? 1;
 
 					console.log(speak);
 
@@ -410,7 +413,7 @@
 										const isEnterPressed = e.key === 'Enter';
 
 										if (isCmdOrCtrlPressed && isEnterPressed) {
-											document.getElementById('save-edit-message-button')?.click();
+											document.getElementById('confirm-edit-message-button')?.click();
 										}
 									}}
 								/>
@@ -418,7 +421,7 @@
 								<div class=" mt-2 mb-1 flex justify-between text-sm font-medium">
 									<div>
 										<button
-											id="close-edit-message-button"
+											id="save-new-message-button"
 											class=" px-4 py-2 bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 border dark:border-gray-700 text-gray-700 dark:text-gray-200 transition rounded-3xl"
 											on:click={() => {
 												saveNewMessageHandler();

+ 21 - 0
src/lib/components/chat/Settings/Audio.svelte

@@ -23,6 +23,10 @@
 	let voices = [];
 	let voice = '';
 
+	// Audio speed control
+	let speechRate = 1;
+	const speedOptions = [2, 1.75, 1.5, 1.25, 1, 0.75, 0.5];
+
 	const getVoices = async () => {
 		if ($config.audio.tts.engine === '') {
 			const getVoicesLoop = setInterval(async () => {
@@ -56,6 +60,7 @@
 	};
 
 	onMount(async () => {
+		speechRate = $settings.audio?.tts?.speedRate ?? 1;
 		conversationMode = $settings.conversationMode ?? false;
 		speechAutoSend = $settings.speechAutoSend ?? false;
 		responseAutoPlayback = $settings.responseAutoPlayback ?? false;
@@ -83,6 +88,7 @@
 					engine: STTEngine !== '' ? STTEngine : undefined
 				},
 				tts: {
+					speedRate: speechRate,
 					voice: voice !== '' ? voice : undefined,
 					defaultVoice: $config?.audio?.tts?.voice ?? '',
 					nonLocalVoices: $config.audio.tts.engine === '' ? nonLocalVoices : undefined
@@ -153,6 +159,21 @@
 					{/if}
 				</button>
 			</div>
+
+			<div class=" py-0.5 flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">{$i18n.t('Speed Rate')}</div>
+
+				<div class="flex items-center relative">
+					<select
+						class="dark:bg-gray-900 w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
+						bind:value={speechRate}
+					>
+						{#each speedOptions as option}
+							<option value={option} selected={speechRate === option}>{option}x</option>
+						{/each}
+					</select>
+				</div>
+			</div>
 		</div>
 
 		<hr class=" dark:border-gray-850" />