Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
chenpangpang
open-webui
Commits
182ab8b8
Unverified
Commit
182ab8b8
authored
Feb 10, 2024
by
Timothy Jaeryang Baek
Committed by
GitHub
Feb 10, 2024
Browse files
Merge pull request #704 from ollama-webui/tts
feat: tts optimisation
parents
68ed24b7
66f86062
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
87 additions
and
29 deletions
+87
-29
src/lib/components/chat/Messages/ResponseMessage.svelte
src/lib/components/chat/Messages/ResponseMessage.svelte
+70
-29
src/lib/utils/index.ts
src/lib/utils/index.ts
+17
-0
No files found.
src/lib/components/chat/Messages/ResponseMessage.svelte
View file @
182ab8b8
...
@@ -15,6 +15,7 @@
...
@@ -15,6 +15,7 @@
import CodeBlock from './CodeBlock.svelte';
import CodeBlock from './CodeBlock.svelte';
import { synthesizeOpenAISpeech } from '$lib/apis/openai';
import { synthesizeOpenAISpeech } from '$lib/apis/openai';
import { extractSentences } from '$lib/utils';
export let modelfiles = [];
export let modelfiles = [];
export let message;
export let message;
...
@@ -35,8 +36,10 @@
...
@@ -35,8 +36,10 @@
let tooltipInstance = null;
let tooltipInstance = null;
let
a
udio
Map
= {};
let
sentencesA
udio = {};
let speaking = null;
let speaking = null;
let speakingIdx = null;
let loadingSpeech = false;
let loadingSpeech = false;
$: tokens = marked.lexer(message.content);
$: tokens = marked.lexer(message.content);
...
@@ -116,22 +119,68 @@
...
@@ -116,22 +119,68 @@
}
}
};
};
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, 300));
if (Object.keys(sentencesAudio).length - 1 === idx) {
speaking = null;
}
res(e);
};
});
};
const toggleSpeakMessage = async () => {
const toggleSpeakMessage = async () => {
if (speaking) {
if (speaking) {
speechSynthesis.cancel();
speechSynthesis.cancel();
speaking = null;
audioMap[message.id].pause();
sentencesAudio[speakingIdx].pause();
audioMap[message.id].currentTime = 0;
sentencesAudio[speakingIdx].currentTime = 0;
speaking = null;
speakingIdx = null;
} else {
} else {
speaking = true;
speaking = true;
if ($settings?.speech?.engine === 'openai') {
if ($settings?.speech?.engine === 'openai') {
loadingSpeech = true;
loadingSpeech = true;
const sentences = extractSentences(message.content).reduce((mergedTexts, currentText) => {
const lastIndex = mergedTexts.length - 1;
if (lastIndex >= 0) {
const previousText = mergedTexts[lastIndex];
const wordCount = previousText.split(/\s+/).length;
if (wordCount < 2) {
mergedTexts[lastIndex] = previousText + ' ' + currentText;
} else {
mergedTexts.push(currentText);
}
} else {
mergedTexts.push(currentText);
}
return mergedTexts;
}, []);
console.log(sentences);
sentencesAudio = sentences.reduce((a, e, i, arr) => {
a[i] = null;
return a;
}, {});
let lastPlayedAudioPromise = Promise.resolve(); // Initialize a promise that resolves immediately
for (const [idx, sentence] of sentences.entries()) {
const res = await synthesizeOpenAISpeech(
const res = await synthesizeOpenAISpeech(
localStorage.token,
localStorage.token,
$settings?.speech?.speaker,
$settings?.speech?.speaker,
message.co
nten
t
se
nten
ce
).catch((error) => {
).catch((error) => {
toast.error(error);
toast.error(error);
return null;
return null;
...
@@ -140,20 +189,12 @@
...
@@ -140,20 +189,12 @@
if (res) {
if (res) {
const blob = await res.blob();
const blob = await res.blob();
const blobUrl = URL.createObjectURL(blob);
const blobUrl = URL.createObjectURL(blob);
console.log(blobUrl);
loadingSpeech = false;
const audio = new Audio(blobUrl);
const audio = new Audio(blobUrl);
audioMap[message.id] = audio;
sentencesAudio[idx] = audio;
loadingSpeech = false;
audio.onended = () => {
lastPlayedAudioPromise = lastPlayedAudioPromise.then(() => playAudio(idx));
speaking = null;
if ($settings.conversationMode) {
document.getElementById('voice-input-button')?.click();
}
}
};
audio.play().catch((e) => console.error('Error playing audio:', e));
}
}
} else {
} else {
let voices = [];
let voices = [];
...
...
src/lib/utils/index.ts
View file @
182ab8b8
...
@@ -324,3 +324,20 @@ export const isValidHttpUrl = (string) => {
...
@@ -324,3 +324,20 @@ export const isValidHttpUrl = (string) => {
return
url
.
protocol
===
'
http:
'
||
url
.
protocol
===
'
https:
'
;
return
url
.
protocol
===
'
http:
'
||
url
.
protocol
===
'
https:
'
;
};
};
export
const
removeEmojis
=
(
str
)
=>
{
// Regular expression to match emojis
const
emojiRegex
=
/
[\u
D800-
\u
DBFF
][\u
DC00-
\u
DFFF
]
|
\u
D83C
[\u
DC00-
\u
DFFF
]
|
\u
D83D
[\u
DC00-
\u
DE4F
]
/g
;
// Replace emojis with an empty string
return
str
.
replace
(
emojiRegex
,
''
);
};
export
const
extractSentences
=
(
text
)
=>
{
// Split the paragraph into sentences based on common punctuation marks
const
sentences
=
text
.
split
(
/
(?<
=
[
.!?
])
/
);
return
sentences
.
map
((
sentence
)
=>
removeEmojis
(
sentence
.
trim
()))
.
filter
((
sentence
)
=>
sentence
!==
''
);
};
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment