Merge pull request #2804 from v3ucn/CosyVoice TTS API for the CosyVoice

010b3fa49230e98f6e5c2da05f1f987d8016e7aa

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
2 files changed, +208 -0Showing whitespace changes
public/scripts/extensions/tts/cosyvoice.js+206 -0
@@ -0,0 +1,206 @@
1+import { saveTtsProviderSettings } from './index.js';
2+
3+export { CosyVoiceProvider };
4+
5+class CosyVoiceProvider {
6+ //########//
7+ // Config //
8+ //########//
9+
10+ settings;
11+ ready = false;
12+ voices = [];
13+ separator = '. ';
14+ audioElement = document.createElement('audio');
15+
16+ /**
17+ * Perform any text processing before passing to TTS engine.
18+ * @param {string} text Input text
19+ * @returns {string} Processed text
20+ */
21+ processText(text) {
22+ return text;
23+ }
24+
25+ audioFormats = ['wav', 'ogg', 'silk', 'mp3', 'flac'];
26+
27+ languageLabels = {
28+ 'Auto': 'auto',
29+ };
30+
31+ langKey2LangCode = {
32+ 'zh': 'zh-CN',
33+ 'en': 'en-US',
34+ 'ja': 'ja-JP',
35+ 'ko': 'ko-KR',
36+ };
37+
38+ modelTypes = {
39+ CosyVoice: 'CosyVoice',
40+ };
41+
42+ defaultSettings = {
43+ provider_endpoint: 'http://localhost:9880',
44+ format: 'wav',
45+ lang: 'auto',
46+ streaming: false,
47+
48+ };
49+
50+ get settingsHtml() {
51+ let html = `
52+
53+ <label for="tts_endpoint">Provider Endpoint:</label>
54+ <input id="tts_endpoint" type="text" class="text_pole" maxlength="250" height="300" value="${this.defaultSettings.provider_endpoint}"/>
55+ <span>Windows users Use <a target="_blank" href="https://github.com/v3ucn/CosyVoice_For_Windows">CosyVoice_For_Windows</a>(Unofficial).</span><br/>
56+ <span>Macos Users Use <a target="_blank" href="https://github.com/v3ucn/CosyVoice_for_MacOs">CosyVoice_for_MacOs</a>(Unofficial).</span><br/>
57+ <br/>
58+
59+ `;
60+
61+ return html;
62+ }
63+
64+ onSettingsChange() {
65+ // Used when provider settings are updated from UI
66+ this.settings.provider_endpoint = $('#tts_endpoint').val();
67+
68+
69+ saveTtsProviderSettings();
70+ this.changeTTSSettings();
71+ }
72+
73+ async loadSettings(settings) {
74+ // Pupulate Provider UI given input settings
75+ if (Object.keys(settings).length == 0) {
76+ console.info('Using default TTS Provider settings');
77+ }
78+
79+ // Only accept keys defined in defaultSettings
80+ this.settings = this.defaultSettings;
81+
82+ for (const key in settings) {
83+ if (key in this.settings) {
84+ this.settings[key] = settings[key];
85+ } else {
86+ console.debug(`Ignoring non-user-configurable setting: ${key}`);
87+ }
88+ }
89+
90+ // Set initial values from the settings
91+ $('#tts_endpoint').val(this.settings.provider_endpoint);
92+
93+
94+ await this.checkReady();
95+
96+ console.info('ITS: Settings loaded');
97+ }
98+
99+ // Perform a simple readiness check by trying to fetch voiceIds
100+ async checkReady() {
101+ await Promise.allSettled([this.fetchTtsVoiceObjects(), this.changeTTSSettings()]);
102+ }
103+
104+ async onRefreshClick() {
105+ return;
106+ }
107+
108+ //#################//
109+ // TTS Interfaces //
110+ //#################//
111+
112+ async getVoice(voiceName) {
113+
114+
115+
116+ if (this.voices.length == 0) {
117+ this.voices = await this.fetchTtsVoiceObjects();
118+ }
119+
120+
121+
122+ const match = this.voices.filter(
123+ v => v.name == voiceName,
124+ )[0];
125+ console.log(match);
126+ if (!match) {
127+ throw `TTS Voice name ${voiceName} not found`;
128+ }
129+ return match;
130+ }
131+
132+
133+
134+ async generateTts(text, voiceId) {
135+ const response = await this.fetchTtsGeneration(text, voiceId);
136+ return response;
137+ }
138+
139+ //###########//
140+ // API CALLS //
141+ //###########//
142+ async fetchTtsVoiceObjects() {
143+ const response = await fetch(`${this.settings.provider_endpoint}/speakers`);
144+ console.info(response);
145+
146+ if (!response.ok) {
147+ throw new Error(`HTTP ${response.status}: ${await response.json()}`);
148+ }
149+ const responseJson = await response.json();
150+
151+
152+ this.voices = responseJson;
153+
154+ return responseJson;
155+ }
156+
157+ // Each time a parameter is changed, we change the configuration
158+ async changeTTSSettings() {
159+ }
160+
161+ /**
162+ * Fetch TTS generation from the API.
163+ * @param {string} inputText Text to generate TTS for
164+ * @param {string} voiceId Voice ID to use (model_type&speaker_id))
165+ * @returns {Promise<Response|string>} Fetch response
166+ */
167+ async fetchTtsGeneration(inputText, voiceId, lang = null, forceNoStreaming = false) {
168+ console.info(`Generating new TTS for voice_id ${voiceId}`);
169+
170+ const streaming = this.settings.streaming;
171+
172+ const params = {
173+ text: inputText,
174+ speaker: voiceId,
175+ };
176+
177+ if (streaming) {
178+ params['streaming'] = 1;
179+ }
180+
181+ const url = `${this.settings.provider_endpoint}/`;
182+
183+ const response = await fetch(
184+ url,
185+ {
186+ method: 'POST',
187+ headers: {
188+ 'Content-Type': 'application/json',
189+ },
190+ body: JSON.stringify(params), // Convert parameter objects to JSON strings
191+ },
192+ );
193+ if (!response.ok) {
194+ toastr.error(response.statusText, 'TTS Generation Failed');
195+ throw new Error(`HTTP ${response.status}: ${await response.text()}`);
196+ }
197+ return response;
198+ }
199+
200+
201+
202+ // Interface not used
203+ async fetchTtsFromHistory(history_item_id) {
204+ return Promise.resolve(history_item_id);
205+ }
206+}
public/scripts/extensions/tts/index.js+2 -0
@@ -15,6 +15,7 @@ import { VITSTtsProvider } from './vits.js';
1515import { GSVITtsProvider } from './gsvi.js';
1616import { SBVits2TtsProvider } from './sbvits2.js';
1717import { AllTalkTtsProvider } from './alltalk.js';
18+import { CosyVoiceProvider } from './cosyvoice.js';
1819import { SpeechT5TtsProvider } from './speecht5.js';
1920import { AzureTtsProvider } from './azure.js';
2021import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js';
@@ -86,6 +87,7 @@ const ttsProviders = {
8687 AllTalk: AllTalkTtsProvider,
8788 Azure: AzureTtsProvider,
8889 Coqui: CoquiTtsProvider,
90+ 'CosyVoice (Unofficial)': CosyVoiceProvider,
8991 Edge: EdgeTtsProvider,
9092 ElevenLabs: ElevenLabsTtsProvider,
9193 GSVI: GSVITtsProvider,