diff --git a/src/components/settings.tsx b/src/components/settings.tsx
index 5c9e2bce..3fcfa1fb 100644
--- a/src/components/settings.tsx
+++ b/src/components/settings.tsx
@@ -48,6 +48,8 @@ import { OpenAITTSSettingsPage } from './settings/OpenAITTSSettingsPage';
import { PiperSettingsPage } from './settings/PiperSettingsPage';
+import { CoquiLocalSettingsPage } from './settings/CoquiLocalSettingsPage';
+
import { STTBackendPage } from './settings/STTBackendPage';
import { STTWakeWordSettingsPage } from './settings/STTWakeWordSettingsPage';
@@ -101,6 +103,9 @@ export const Settings = ({
const [piperUrl, setPiperUrl] = useState(config("piper_url"));
+ const [coquiLocalUrl, setCoquiLocalUrl] = useState(config("coquiLocal_url"));
+ const [coquiLocalVoiceId, setCoquiLocalVoiceId] = useState(config("coquiLocal_voiceid"));
+
const [visionBackend, setVisionBackend] = useState(config("vision_backend"));
const [visionLlamaCppUrl, setVisionLlamaCppUrl] = useState(config("vision_llamacpp_url"));
const [visionOllamaUrl, setVisionOllamaUrl] = useState(config("vision_ollama_url"));
@@ -214,6 +219,7 @@ export const Settings = ({
coquiApiKey, coquiVoiceId,
openAITTSApiKey, openAITTSUrl, openAITTSModel, openAITTSVoice,
piperUrl,
+ coquiLocalUrl,coquiLocalVoiceId,
visionBackend,
visionLlamaCppUrl,
visionOllamaUrl, visionOllamaModel,
@@ -253,7 +259,7 @@ export const Settings = ({
case 'tts':
return ;
case 'stt':
@@ -411,6 +417,15 @@ export const Settings = ({
setPiperUrl={setPiperUrl}
setSettingsUpdated={setSettingsUpdated}
/>
+
+ case 'coquiLocal_settings':
+ return
case'stt_backend':
return void;
+ setSettingsUpdated: (updated: boolean) => void;
+ setCoquiLocalVoiceId: (key: string) => void;
+}) {
+ const { t } = useTranslation();
+
+ return (
+
+ { config("tts_backend") !== "coquiLocal" && (
+
+ {t("not_using_alert", "You are not currently using {{name}} as your {{what}} backend. These settings will not be used.", {name: t("CoquiLocal"), what: t("TTS")})}
+
+ ) }
+
+ -
+
+ ) => {
+ setCoquiLocalUrl(event.target.value);
+ updateConfig("coquiLocal_url", event.target.value);
+ setSettingsUpdated(true);
+ }}
+ />
+
-
+
+ ) => {
+ event.preventDefault();
+ setCoquiLocalVoiceId(event.target.value);
+ updateConfig("coquiLocal_voiceid", event.target.value);
+ setSettingsUpdated(true);
+ }}
+ />
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/settings/TTSBackendPage.tsx b/src/components/settings/TTSBackendPage.tsx
index d495743c..3540ed30 100644
--- a/src/components/settings/TTSBackendPage.tsx
+++ b/src/components/settings/TTSBackendPage.tsx
@@ -11,6 +11,7 @@ const ttsEngines = [
{key: "openai_tts", label: "OpenAI TTS"},
{key: "localXTTS", label: "Local XTTS"}, // Our local TTS endpoint (XTTS based)
{key: "piper", label: "Piper"},
+ {key: "coquiLocal", label: "Coqui Local"},
];
function idToTitle(id: string): string {
@@ -57,7 +58,7 @@ export function TTSBackendPage({
- { ["elevenlabs", "speecht5", "coqui", "openai_tts", "piper"].includes(ttsBackend) && (
+ { ["elevenlabs", "speecht5", "coqui", "openai_tts", "piper", "coquiLocal"].includes(ttsBackend) && (
;
case 'openai_tts_settings': return ;
case 'piper_settings': return ;
+ case 'coquiLocal_settings': return ;
case 'stt_backend': return ;
case 'stt_wake_word': return ;
@@ -197,6 +198,7 @@ function getLabelFromPage(page: string): string {
case 'coqui_settings': return t('Coqui');
case 'openai_tts_settings': return t('OpenAI');
case 'piper_settings': return t('Piper');
+ case 'coquiLocal_settings': return t('coquiLocal');
case 'vision_backend': return t('Vision Backend');
case 'vision_llamacpp_settings': return t('LLama.cpp');
diff --git a/src/features/chat/chat.ts b/src/features/chat/chat.ts
index 4d66d2d6..3d0fbe5a 100644
--- a/src/features/chat/chat.ts
+++ b/src/features/chat/chat.ts
@@ -10,6 +10,7 @@ import { getWindowAiChatResponseStream } from './windowAiChat';
import { getOllamaChatResponseStream, getOllamaVisionChatResponse } from './ollamaChat';
import { getKoboldAiChatResponseStream } from './koboldAiChat';
+import { coquiLocal} from "@/features/coquiLocal/coquiLocal";
import { piper} from "@/features/piper/piper";
import { elevenlabs } from "@/features/elevenlabs/elevenlabs";
import { coqui } from "@/features/coqui/coqui";
@@ -447,6 +448,10 @@ export class Chat {
const voice = await piper(talk.message);
return voice.audio;
}
+ case 'coquiLocal': {
+ const voice = await coquiLocal(talk.message);
+ return voice.audio;
+ }
}
} catch (e: any) {
console.error(e.toString());
diff --git a/src/features/coquiLocal/coquiLocal.ts b/src/features/coquiLocal/coquiLocal.ts
new file mode 100644
index 00000000..bdb2c201
--- /dev/null
+++ b/src/features/coquiLocal/coquiLocal.ts
@@ -0,0 +1,29 @@
+import { config } from '@/utils/config';
+
+export async function coquiLocal(
+ message: string,
+) {
+
+ const voiceId = config("coquiLocal_voiceid");
+ if (!voiceId) {
+ throw new Error("Invalid CoquiLocal TTS Voice Id");
+ }
+
+ try {
+ const res = await fetch(config("coquiLocal_url"), {
+ method: 'POST',
+ headers: {
+ 'text': message,
+ 'speaker-id': config("coquiLocal_voiceid"),
+ }
+ });
+
+ const data = await res.arrayBuffer()
+ return { audio: data };
+
+ } catch (error) {
+
+ console.error('Error in coquiLocal:', error);
+ throw error;
+ }
+}
\ No newline at end of file
diff --git a/src/utils/config.ts b/src/utils/config.ts
index 02a66310..be963bcf 100644
--- a/src/utils/config.ts
+++ b/src/utils/config.ts
@@ -39,6 +39,8 @@ const defaults = {
openai_tts_model: process.env.NEXT_PUBLIC_OPENAI_TTS_MODEL ?? 'tts-1',
openai_tts_voice: process.env.NEXT_PUBLIC_OPENAI_TTS_VOICE ?? 'nova',
piper_url: process.env.NEXT_PUBLIC_PIPER_URL ?? 'http://localhost:5000',
+ coquiLocal_url: process.env.NEXT_PUBLIC_COQUILOCAL_URL ?? 'http://localhost:5002/api/tts',
+ coquiLocal_voiceid: process.env.NEXT_PUBLIC_COQUILOCAL_VOICEID ?? 'p240',
elevenlabs_apikey: process.env.NEXT_PUBLIC_ELEVENLABS_APIKEY ??'',
elevenlabs_voiceid: process.env.NEXT_PUBLIC_ELEVENLABS_VOICEID ?? '21m00Tcm4TlvDq8ikWAM',
elevenlabs_model: process.env.NEXT_PUBLIC_ELEVENLABS_MODEL ?? 'eleven_monolingual_v1',