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}
+ {/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