diff --git a/src/lib/Setting/Pages/OtherBotSettings.svelte b/src/lib/Setting/Pages/OtherBotSettings.svelte index 197176ad..d098445a 100644 --- a/src/lib/Setting/Pages/OtherBotSettings.svelte +++ b/src/lib/Setting/Pages/OtherBotSettings.svelte @@ -128,6 +128,9 @@ VOICEVOX URL +NovelAI API key + + {language.emotionImage} {language.emotionMethod} diff --git a/src/lib/SideBars/CharConfig.svelte b/src/lib/SideBars/CharConfig.svelte index 51d05dfe..5092502d 100644 --- a/src/lib/SideBars/CharConfig.svelte +++ b/src/lib/SideBars/CharConfig.svelte @@ -15,7 +15,7 @@ import Help from "../Others/Help.svelte"; import RegexData from "./Scripts/RegexData.svelte"; import { exportChar, shareRisuHub } from "src/ts/characterCards"; - import { getElevenTTSVoices, getWebSpeechTTSVoices, getVOICEVOXVoices, oaiVoices } from "src/ts/process/tts"; + import { getElevenTTSVoices, getWebSpeechTTSVoices, getVOICEVOXVoices, oaiVoices, getNovelAIVoices, FixNAITTS } from "src/ts/process/tts"; import { checkCharOrder, getFileSrc } from "src/ts/storage/globalApi"; import { addGroupChar, rmCharFromGroup } from "src/ts/process/group"; import RealmUpload from "../UI/Realm/RealmUpload.svelte"; @@ -27,6 +27,7 @@ import OptionInput from "../UI/GUI/OptionInput.svelte"; import RegexList from "./Scripts/RegexList.svelte"; import TriggerList from "./Scripts/TriggerList.svelte"; + let subMenu = 0 let openHubUpload = false @@ -130,12 +131,20 @@ } } } + } onDestroy(unsub); $:licensed = (currentChar.type === 'character') ? currentChar.data.license : '' + $: if (currentChar.data.ttsMode === 'novelai' && (currentChar.data as character).naittsConfig === undefined) { + (currentChar.data as character).naittsConfig = { + customvoice: false, + voice: 'Aini', + version: 'v2' + }; + } {#if licensed !== 'private'} @@ -542,6 +551,7 @@ Web Speech VOICEVOX OpenAI + NovelAI @@ -600,6 +610,31 @@ Intonation scale To use VOICEVOX, you need to run a colab and put the localtunnel URL in "Settings → Other Bots". https://colab.research.google.com/drive/1tyeXJSklNfjW-aZJAib1JfgOMFarAwze + {:else if currentChar.data.ttsMode === 'novelai'} + Custom Voice Seed + + {#if !currentChar.data.naittsConfig.customvoice} + Voice + + {#await getNovelAIVoices() then voices} + {#each voices as voiceGroup} + + {#each voiceGroup.voices as voice} + {voice} + {/each} + + {/each} + {/await} + + {:else} + Voice + + {/if} + Version + + v1 + v2 + {/if} {#if currentChar.data.ttsMode === 'openai'} OpenAI TTS uses your OpenAI key on the chat model section @@ -610,7 +645,7 @@ {/each} {/if} - {#if currentChar.data.ttsMode === 'webspeech' || currentChar.data.ttsMode === 'elevenlab' || currentChar.data.ttsMode === 'VOICEVOX'} + {#if currentChar.data.ttsMode === 'webspeech' || currentChar.data.ttsMode === 'elevenlab' || currentChar.data.ttsMode === 'VOICEVOX' || currentChar.data.ttsMode === 'novelai'}
diff --git a/src/ts/process/tts.ts b/src/ts/process/tts.ts index 1aeee32f..f6d3a0d8 100644 --- a/src/ts/process/tts.ts +++ b/src/ts/process/tts.ts @@ -139,8 +139,29 @@ export async function sayTTS(character:character,text:string) { } } + case 'novelai': { + const audioContext = new AudioContext(); + const response = await fetch(`https://api.novelai.net/ai/generate-voice?text=${text}&voice=-1&seed=${character.naittsConfig.voice}&opus=false&version=${character.naittsConfig.version}`, { + method: 'GET', + headers: { + "Authorization": "Bearer " + db.NAIApiKey, + } + }); + + if (response.status === 200 && response.headers.get('content-type') === 'audio/mpeg') { + const audioBuffer = await response.arrayBuffer(); + audioContext.decodeAudioData(audioBuffer, (decodedData) => { + const sourceNode = audioContext.createBufferSource(); + sourceNode.buffer = decodedData; + sourceNode.connect(audioContext.destination); + sourceNode.start(); + }); + } else { + alertError("Error fetching or decoding audio data"); + } + break; + } } - } export const oaiVoices = [ @@ -189,4 +210,29 @@ export async function getVOICEVOXVoices() { }) speakersInfo.unshift({ name: "None", list: null}) return speakersInfo; +} + +export async function getNovelAIVoices(){ + return [ + { + gender: "UNISEX", + voices: ['Anananan'] + }, + { + gender: "FEMALE", + voices: ['Aini', 'Orea', 'Claea', 'Lim', 'Aurae', 'Naia'] + }, + { + gender: "MALE", + voices: ['Aulon', 'Elei', 'Ogma', 'Raid', 'Pega', 'Lam'] + } + ]; +} + +export async function FixNAITTS(data:character){ + if (data.naittsConfig === undefined){ + data.naittsConfig.voice = 'Anananan' + } + + return data } \ No newline at end of file diff --git a/src/ts/storage/database.ts b/src/ts/storage/database.ts index 67bcafca..11b280c3 100644 --- a/src/ts/storage/database.ts +++ b/src/ts/storage/database.ts @@ -619,6 +619,11 @@ export interface character{ INTONATION_SCALE?: number VOLUME_SCALE?: number } + naittsConfig?:{ + customvoice?: boolean + voice?: string + version?: string + } supaMemory?:boolean additionalAssets?:[string, string, string][] ttsReadOnlyQuoted?:boolean