+
+ Text Split Method
+
+ Cut 0 (No splitting)
+ Cut 1 (Split every 4 sentences)
+ Cut 2 (Split every 50 characters)
+ Cut 3 (Split by Chinese periods)
+ Cut 4 (Split by English periods)
+ Cut 5 (Split by various punctuation marks)
+
{/if}
{#if currentChar.data.ttsMode}
diff --git a/src/ts/process/tts.ts b/src/ts/process/tts.ts
index db3e32c2..6ecbb755 100644
--- a/src/ts/process/tts.ts
+++ b/src/ts/process/tts.ts
@@ -2,7 +2,7 @@ import { get } from "svelte/store";
import { alertError } from "../alert";
import { DataBase, type character } from "../storage/database";
import { runTranslator, translateVox } from "../translator/translator";
-import { globalFetch } from "../storage/globalApi";
+import { globalFetch, loadAsset } from "../storage/globalApi";
import { language } from "src/lang";
import { getCurrentCharacter, sleep } from "../util";
import { registerOnnxModel, runVITS } from "./transformers";
@@ -27,7 +27,7 @@ export async function sayTTS(character:character,text:string) {
text = text.replace(/\*/g,'')
if(character.ttsReadOnlyQuoted){
- const matches = text.match(/"(.*?)"/g)
+ const matches = text.match(/["「](.*?)["」]/g)
if(matches && matches.length > 0){
text = matches.map(match => match.slice(1, -1)).join("");
}
@@ -231,12 +231,71 @@ export async function sayTTS(character:character,text:string) {
case 'vits':{
await runVITS(text, character.vits)
}
+ case 'gptsovits':{
+ const audioContext = new AudioContext();
+
+ const audio: Uint8Array = await loadAsset(character.gptSoVitsConfig.ref_audio_data.assetId);
+ const base64Audio = btoa(new Uint8Array(audio).reduce((data, byte) => data + String.fromCharCode(byte), ''));
+
+ const body = {
+ text: text,
+ text_lang: character.gptSoVitsConfig.text_lang,
+ ref_audio_path: character.gptSoVitsConfig.ref_audio_path + '/public/audio/' + character.gptSoVitsConfig.ref_audio_data.fileName,
+ ref_audio_name: character.gptSoVitsConfig.ref_audio_data.fileName,
+ ref_audio_data: base64Audio,
+ prompt_text: undefined,
+ prompt_lang: character.gptSoVitsConfig.prompt_lang,
+ top_p: character.gptSoVitsConfig.top_p,
+ temperature: character.gptSoVitsConfig.temperature,
+ speed_factor: character.gptSoVitsConfig.speed,
+ top_k: character.gptSoVitsConfig.top_k,
+ text_split_method: character.gptSoVitsConfig.text_split_method,
+ parallel_infer: false,
+ }
+
+ if (character.gptSoVitsConfig.use_prompt){
+ body.prompt_text = character.gptSoVitsConfig.prompt
+ }
+ console.log(body)
+
+ const response = await globalFetch(`${character.gptSoVitsConfig.url}/tts`, {
+ method: 'POST',
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: body,
+ rawResponse: true,
+ })
+ console.log(response)
+
+ if (response.ok) {
+ const audioBuffer = response.data.buffer;
+ audioContext.decodeAudioData(audioBuffer, (decodedData) => {
+ const sourceNode = audioContext.createBufferSource();
+ sourceNode.buffer = decodedData;
+
+ const gainNode = audioContext.createGain();
+ gainNode.gain.value = character.gptSoVitsConfig.volume || 1.0;
+
+ sourceNode.connect(gainNode);
+ gainNode.connect(audioContext.destination);
+
+ sourceNode.start();
+ });
+ } else {
+ const textBuffer: Uint8Array = response.data.buffer
+ const text = Buffer.from(textBuffer).toString('utf-8')
+ throw new Error(text);
+ }
+ }
}
} catch (error) {
alertError(`TTS Error: ${error}`)
}
}
+
+
export const oaiVoices = [
'alloy', 'echo', 'fable', 'onyx', 'nova', 'shimmer'
]
diff --git a/src/ts/storage/database.ts b/src/ts/storage/database.ts
index b4f80b2b..1f5f8b26 100644
--- a/src/ts/storage/database.ts
+++ b/src/ts/storage/database.ts
@@ -803,16 +803,23 @@ export interface character{
version?: string
}
gptSoVitsConfig?:{
- ref_audio_data?:string
+ url?:string
+ ref_audio_path?:string
+ ref_audio_data?: {
+ fileName:string
+ assetId:string
+ }
+ volume?:number
text_lang?: "auto" | "auto_yue" | "en" | "zh" | "ja" | "yue" | "ko" | "all_zh" | "all_ja" | "all_yue" | "all_ko"
text?:string
+ use_prompt?:boolean
prompt?:string | null
- prompt_lang?:string
+ prompt_lang?: "auto" | "auto_yue" | "en" | "zh" | "ja" | "yue" | "ko" | "all_zh" | "all_ja" | "all_yue" | "all_ko"
top_p?:number
temperature?:number
speed?:number
top_k?:number
- text_split_method?:string
+ text_split_method?: "cut0" | "cut1" | "cut2" | "cut3" | "cut4" | "cut5"
}
supaMemory?:boolean
additionalAssets?:[string, string, string][]