echogarden 2.0.14 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +1 -1
  2. package/data/lexicons/heteronyms.en.json +47 -5
  3. package/data/lexicons/words.en.json +184 -0
  4. package/data/schemas/options.json +64 -0
  5. package/dist/api/Synthesis.d.ts +10 -1
  6. package/dist/api/Synthesis.js +76 -9
  7. package/dist/api/Synthesis.js.map +1 -1
  8. package/dist/nlp/IPA.d.ts +3 -3
  9. package/dist/nlp/Lexicon.js +2 -0
  10. package/dist/nlp/Lexicon.js.map +1 -1
  11. package/dist/recognition/WhisperSTT.js +1 -1
  12. package/dist/recognition/WhisperSTT.js.map +1 -1
  13. package/dist/synthesis/GnuSpeechTTS.d.ts +12 -0
  14. package/dist/synthesis/GnuSpeechTTS.js +36 -0
  15. package/dist/synthesis/GnuSpeechTTS.js.map +1 -0
  16. package/dist/synthesis/KokoroTTS.d.ts +22 -0
  17. package/dist/synthesis/KokoroTTS.js +595 -0
  18. package/dist/synthesis/KokoroTTS.js.map +1 -0
  19. package/dist/synthesis/OpenAICloudTTS.js +15 -0
  20. package/dist/synthesis/OpenAICloudTTS.js.map +1 -1
  21. package/dist/synthesis/VitsTTS.js +6 -1
  22. package/dist/synthesis/VitsTTS.js.map +1 -1
  23. package/dist/utilities/FileReader.js +1 -1
  24. package/dist/utilities/PackageManager.js +4 -0
  25. package/dist/utilities/PackageManager.js.map +1 -1
  26. package/dist/utilities/Utilities.d.ts +1 -0
  27. package/dist/utilities/Utilities.js +8 -0
  28. package/dist/utilities/Utilities.js.map +1 -1
  29. package/dist/utilities/WasmMemoryManager.d.ts +1 -1
  30. package/docs/Development.md +1 -1
  31. package/docs/Engines.md +4 -1
  32. package/docs/Licenses.md +3 -1
  33. package/docs/Options.md +9 -5
  34. package/docs/Tasklist.md +1 -1
  35. package/package.json +13 -12
  36. package/src/api/Synthesis.ts +132 -10
  37. package/src/nlp/Lexicon.ts +3 -0
  38. package/src/recognition/WhisperSTT.ts +1 -1
  39. package/src/synthesis/GnuSpeechTTS.ts +40 -0
  40. package/src/synthesis/KokoroTTS.ts +693 -0
  41. package/src/synthesis/OpenAICloudTTS.ts +15 -0
  42. package/src/synthesis/VitsTTS.ts +6 -1
  43. package/src/utilities/FileReader.ts +1 -1
  44. package/src/utilities/PackageManager.ts +5 -0
  45. package/src/utilities/Utilities.ts +10 -0
package/README.md CHANGED
@@ -11,7 +11,7 @@ Echogarden is an easy-to-use speech toolset that includes a variety of speech pr
11
11
 
12
12
  ## Features
13
13
 
14
- * **Text-to-speech** using the [VITS](https://github.com/jaywalnut310/vits) neural architecture, and [15 other offline and online engines](docs/Engines.md), including cloud services by [Google](https://cloud.google.com/text-to-speech), [Microsoft](https://azure.microsoft.com/en-us/products/ai-services/text-to-speech/), [Amazon](https://aws.amazon.com/polly/), [OpenAI](https://platform.openai.com/) and [Elevenlabs](https://elevenlabs.io/)
14
+ * **Text-to-speech** using high-quality [Kokoro](https://github.com/hexgrad/kokoro) and [VITS](https://github.com/jaywalnut310/vits) offline voices, for many languages and dialects, and [16 other offline and online engines](docs/Engines.md), including cloud services by [Google](https://cloud.google.com/text-to-speech), [Microsoft](https://azure.microsoft.com/en-us/products/ai-services/text-to-speech/), [Amazon](https://aws.amazon.com/polly/), [OpenAI](https://platform.openai.com/) and [Elevenlabs](https://elevenlabs.io/)
15
15
  * **Speech-to-text** using a custom TypeScript/ONNX port of the [OpenAI Whisper](https://openai.com/research/whisper) speech recognition architecture, [whisper.cpp](https://github.com/ggerganov/whisper.cpp), and [several other engines](docs/Engines.md), including cloud services by [Google](https://cloud.google.com/speech-to-text), [Microsoft](https://azure.microsoft.com/en-us/products/ai-services/speech-to-text/), [Amazon](https://aws.amazon.com/transcribe/) and [OpenAI](https://platform.openai.com/)
16
16
  * **Speech-to-transcript alignment** using several variants of [dynamic time warping](https://en.wikipedia.org/wiki/Dynamic_time_warping) (DTW, DTW-RA), including support for multi-pass (hierarchical) processing, or via guided decoding using Whisper recognition models. Supports 100+ languages
17
17
  * **Speech-to-text translation**, translates speech in any of the [98 languages](https://platform.openai.com/docs/guides/speech-to-text/supported-languages) supported by Whisper, to English, with near word-level timing for the translated transcript
@@ -167,6 +167,48 @@
167
167
  "example": "There's no use."
168
168
  }
169
169
  ],
170
+ "close": [
171
+ {
172
+ "pronunciation": {
173
+ "espeak": {
174
+ "en-us": "k l ˈoʊ z",
175
+ "en-gb-x-rp": "k l ˈoʊ z"
176
+ }
177
+ },
178
+
179
+ "pos": [
180
+ "VB"
181
+ ],
182
+
183
+ "precededBy": [
184
+ "to", "we", "you", "they", "who", "i", "me", "will", "also", "don't", "didn't", "not", "doesn't", "please", "we'll", "would", "wouldn't", "should", "shouldn't", "must", "mustn't", "shall", "can", "can't", "may", "might", "can", "could", "couldn't"
185
+ ],
186
+
187
+ "notPrecededBy": [
188
+ "the", "a", "be", "for", "no", "in", "of", "make", "its", "single", "their", "good", "making", "her", "his", "its", "our", "your", "my", "any", "personal"
189
+ ],
190
+
191
+ "succeededBy": [
192
+ "the", "a", "an", "their", "it", "this", "them", "his", "her", "our", "your", "its", "these", "all", "my", "any", "those", "some", "one", "more", "every", "up", "down", "other"
193
+ ],
194
+
195
+ "notSucceededBy": [
196
+ "to", "with"
197
+ ],
198
+
199
+ "example": "I will close the door."
200
+ },
201
+ {
202
+ "pronunciation": {
203
+ "espeak": {
204
+ "en-us": "k l ˈoʊ s",
205
+ "en-gb-x-rp": "k l ˈoʊ s"
206
+ }
207
+ },
208
+
209
+ "example": "This seems very close."
210
+ }
211
+ ],
170
212
  "live": [
171
213
  {
172
214
  "pronunciation": {
@@ -226,7 +268,7 @@
226
268
  ],
227
269
 
228
270
  "notPrecededBy": [
229
- "the", "to", "their", "our", "these", "those", "some", "saved", "matter", "few", "some", "many", "innocent"
271
+ "the", "to", "their", "our", "these", "those", "some", "saved", "matter", "few", "some", "many", "innocent", "improve", "better"
230
272
  ],
231
273
 
232
274
  "succeededBy": [
@@ -854,7 +896,7 @@
854
896
  ],
855
897
 
856
898
  "succeededBy": [
857
- "down", "up", "through", "the", "it", "each", "a", "an", "you", "me", "us", "them", "your", "their", "his", "her", "herself", "himself", "itself", "everybody", "our"
899
+ "down", "up", "out", "through", "the", "it", "each", "a", "an", "you", "me", "us", "them", "your", "their", "his", "her", "herself", "himself", "itself", "everybody", "our"
858
900
  ],
859
901
 
860
902
  "notSucceededBy": [
@@ -1300,11 +1342,11 @@
1300
1342
  ],
1301
1343
 
1302
1344
  "precededBy": [
1303
- "to", "i", "me", "us", "they", "we", "you", "will", "would", "can", "can't", "may", "might", "would", "wouldn't", "won't", "don't", "doesn't", "should", "shouldn't", "could", "couldn't", "must", "mustn't", "shall", "did", "didn't", "then"
1345
+ "to", "i", "me", "us", "they", "we", "you", "will", "would", "can", "can't", "may", "might", "would", "wouldn't", "won't", "don't", "doesn't", "should", "shouldn't", "could", "couldn't", "must", "mustn't", "shall", "did", "didn't", "then", "people"
1304
1346
  ],
1305
1347
 
1306
1348
  "notPrecededBy": [
1307
- "of", "for", "this", "similar", "with", "criminal", "dangerous", "ethical", "unethical", "good", "future", "disorderly"
1349
+ "of", "for", "this", "similar", "with", "criminal", "dangerous", "ethical", "unethical", "good", "future", "disorderly", "his", "her", "our", "my"
1308
1350
  ],
1309
1351
 
1310
1352
  "succeededBy": [
@@ -1629,7 +1671,7 @@
1629
1671
  ],
1630
1672
 
1631
1673
  "precededBy": [
1632
- "the", "a", "all", "his", "her", "its", "our", "be", "very"
1674
+ "very", "quite"
1633
1675
  ],
1634
1676
 
1635
1677
  "notPrecededBy": [
@@ -0,0 +1,184 @@
1
+ {
2
+ "en": {
3
+ "vs.": [
4
+ {
5
+ "pronunciation": {
6
+ "espeak": {
7
+ "en-us": "v ˈɜː s ᵻ z",
8
+ "en-gb-x-rp": "v ˈɜː s ɪ z"
9
+ }
10
+ }
11
+ }
12
+ ],
13
+ "inherently": [
14
+ {
15
+ "pronunciation": {
16
+ "espeak": {
17
+ "en-us": "ɪ n h ˈɛ ɹ ə n t l i",
18
+ "en-gb-x-rp": "ɪ n h ˈɛ ɹ ə n t l i"
19
+ }
20
+ }
21
+ }
22
+ ],
23
+ "subsection": [
24
+ {
25
+ "pronunciation": {
26
+ "espeak": {
27
+ "en-us": "s ˈʌ b s ɛ k ʃ ə n",
28
+ "en-gb-x-rp": "s ˈʌ b s ɛ k ʃ ə n"
29
+ }
30
+ }
31
+ }
32
+ ],
33
+ "subsections": [
34
+ {
35
+ "pronunciation": {
36
+ "espeak": {
37
+ "en-us": "s ˈʌ b s ɛ k ʃ ə n z",
38
+ "en-gb-x-rp": "s ˈʌ b s ɛ k ʃ ə n z"
39
+ }
40
+ }
41
+ }
42
+ ],
43
+ "subvector": [
44
+ {
45
+ "pronunciation": {
46
+ "espeak": {
47
+ "en-us": "s ˈə b v ɛ k t ɚ",
48
+ "en-gb-x-rp": "s ˈə b v ɛ k t ɐ"
49
+ }
50
+ }
51
+ }
52
+ ],
53
+ "subvectors": [
54
+ {
55
+ "pronunciation": {
56
+ "espeak": {
57
+ "en-us": "s ˈə b v ɛ k t ɚ z",
58
+ "en-gb-x-rp": "s ˈə b v ɛ k t ɐ z"
59
+ }
60
+ }
61
+ }
62
+ ],
63
+ "recommender": [
64
+ {
65
+ "pronunciation": {
66
+ "espeak": {
67
+ "en-us": "ɹ ˌiː k ə m ˈɛ n d ɚ",
68
+ "en-gb-x-rp": "ɹ ˌiː k ə m ˈɛ n d ɐ"
69
+ }
70
+ }
71
+ }
72
+ ],
73
+ "representation": [
74
+ {
75
+ "pronunciation": {
76
+ "espeak": {
77
+ "en-us": "ɹ ˌɛ p ɹ ə z ɛ n t ˈeɪ ʃ ə n",
78
+ "en-gb-x-rp": "ɹ ˌɛ p ɹ ᵻ z ɛ n t ˈeɪ ʃ ə n"
79
+ }
80
+ }
81
+ }
82
+ ],
83
+ "representations": [
84
+ {
85
+ "pronunciation": {
86
+ "espeak": {
87
+ "en-us": "ɹ ˌɛ p ɹ ə z ɛ n t ˈeɪ ʃ ə n z",
88
+ "en-gb-x-rp": "ɹ ˌɛ p ɹ ᵻ z ɛ n t ˈeɪ ʃ ə n z"
89
+ }
90
+ }
91
+ }
92
+ ],
93
+ "residual": [
94
+ {
95
+ "pronunciation": {
96
+ "espeak": {
97
+ "en-us": "ɹ ə z ˈɪ dʒ uː əl",
98
+ "en-gb-x-rp": "ɹ ᵻ z ˈɪ dʒ ʊ l"
99
+ }
100
+ }
101
+ }
102
+ ],
103
+ "residuals": [
104
+ {
105
+ "pronunciation": {
106
+ "espeak": {
107
+ "en-us": "ɹ ə z ˈɪ dʒ uː əl z",
108
+ "en-gb-x-rp": "ɹ ᵻ z ˈɪ dʒ ʊ l z"
109
+ }
110
+ }
111
+ }
112
+ ],
113
+ "kokoro": [
114
+ {
115
+ "pronunciation": {
116
+ "espeak": {
117
+ "en-us": "k ˈoʊ k ə ɹ oʊ",
118
+ "en-gb-x-rp": "k ˈəʊ k ə ɹ əʊ"
119
+ }
120
+ }
121
+ }
122
+ ],
123
+ "query": [
124
+ {
125
+ "pronunciation": {
126
+ "espeak": {
127
+ "en-us": "k w ˈɛ ɹ i",
128
+ "en-gb-x-rp": "k w ˈiə ɹ i"
129
+ }
130
+ }
131
+ }
132
+ ],
133
+ "queries": [
134
+ {
135
+ "pronunciation": {
136
+ "espeak": {
137
+ "en-us": "k w ˈɛ ɹ i z",
138
+ "en-gb-x-rp": "k w ˈiə ɹ i z"
139
+ }
140
+ }
141
+ }
142
+ ],
143
+ "jupyter": [
144
+ {
145
+ "pronunciation": {
146
+ "espeak": {
147
+ "en-us": "dʒ ˈuː p ɪ ɾ ɚ",
148
+ "en-gb-x-rp": "dʒ ˈuː p ɪ t ɐ"
149
+ }
150
+ }
151
+ }
152
+ ],
153
+ "movies": [
154
+ {
155
+ "pronunciation": {
156
+ "espeak": {
157
+ "en-us": "m ˈuː v i z",
158
+ "en-gb-x-rp": "m ˈuː v i z"
159
+ }
160
+ }
161
+ }
162
+ ],
163
+ "preprocessing": [
164
+ {
165
+ "pronunciation": {
166
+ "espeak": {
167
+ "en-us": "p ɹ ˈi p ɹ ˌɑː s ɛ s ɪ ŋ",
168
+ "en-gb-x-rp": "p ɹ ˈi p ɹ ˌəʊ s ɛ s ɪ ŋ"
169
+ }
170
+ }
171
+ }
172
+ ],
173
+ "annoy": [
174
+ {
175
+ "pronunciation": {
176
+ "espeak": {
177
+ "en-us": "ə n ˈɔ ɪ",
178
+ "en-gb-x-rp": "ə n ˈɔ ɪ"
179
+ }
180
+ }
181
+ }
182
+ ]
183
+ }
184
+ }
@@ -191,6 +191,22 @@
191
191
  },
192
192
  "additionalProperties": false
193
193
  },
194
+ "kokoro": {
195
+ "type": "object",
196
+ "properties": {
197
+ "provider": {
198
+ "$ref": "#/definitions/OnnxExecutionProvider"
199
+ },
200
+ "model": {
201
+ "type": "string",
202
+ "enum": [
203
+ "82m-v1.0-fp32",
204
+ "82m-v1.0-quantized"
205
+ ]
206
+ }
207
+ },
208
+ "additionalProperties": false
209
+ },
194
210
  "pico": {
195
211
  "type": "object",
196
212
  "additionalProperties": false
@@ -199,6 +215,21 @@
199
215
  "type": "object",
200
216
  "additionalProperties": false
201
217
  },
218
+ "gnuspeech": {
219
+ "type": "object",
220
+ "properties": {
221
+ "tempo": {
222
+ "type": "number"
223
+ },
224
+ "controlRate": {
225
+ "type": "number"
226
+ },
227
+ "debug": {
228
+ "type": "boolean"
229
+ }
230
+ },
231
+ "additionalProperties": false
232
+ },
202
233
  "espeak": {
203
234
  "type": "object",
204
235
  "properties": {
@@ -376,8 +407,10 @@
376
407
  "type": "string",
377
408
  "enum": [
378
409
  "vits",
410
+ "kokoro",
379
411
  "pico",
380
412
  "flite",
413
+ "gnuspeech",
381
414
  "espeak",
382
415
  "sam",
383
416
  "sapi",
@@ -1471,6 +1504,22 @@
1471
1504
  },
1472
1505
  "additionalProperties": false
1473
1506
  },
1507
+ "kokoro": {
1508
+ "type": "object",
1509
+ "properties": {
1510
+ "provider": {
1511
+ "$ref": "#/definitions/OnnxExecutionProvider"
1512
+ },
1513
+ "model": {
1514
+ "type": "string",
1515
+ "enum": [
1516
+ "82m-v1.0-fp32",
1517
+ "82m-v1.0-quantized"
1518
+ ]
1519
+ }
1520
+ },
1521
+ "additionalProperties": false
1522
+ },
1474
1523
  "pico": {
1475
1524
  "type": "object",
1476
1525
  "additionalProperties": false
@@ -1479,6 +1528,21 @@
1479
1528
  "type": "object",
1480
1529
  "additionalProperties": false
1481
1530
  },
1531
+ "gnuspeech": {
1532
+ "type": "object",
1533
+ "properties": {
1534
+ "tempo": {
1535
+ "type": "number"
1536
+ },
1537
+ "controlRate": {
1538
+ "type": "number"
1539
+ },
1540
+ "debug": {
1541
+ "type": "boolean"
1542
+ }
1543
+ },
1544
+ "additionalProperties": false
1545
+ },
1482
1546
  "espeak": {
1483
1547
  "type": "object",
1484
1548
  "properties": {
@@ -14,7 +14,7 @@ export interface SynthesisResult {
14
14
  language: string;
15
15
  voice: string;
16
16
  }
17
- export type SynthesisEngine = 'vits' | 'pico' | 'flite' | 'espeak' | 'sam' | 'sapi' | 'msspeech' | 'coqui-server' | 'google-cloud' | 'microsoft-azure' | 'amazon-polly' | 'openai-cloud' | 'elevenlabs' | 'google-translate' | 'microsoft-edge' | 'streamlabs-polly';
17
+ export type SynthesisEngine = 'vits' | 'kokoro' | 'pico' | 'flite' | 'gnuspeech' | 'espeak' | 'sam' | 'sapi' | 'msspeech' | 'coqui-server' | 'google-cloud' | 'microsoft-azure' | 'amazon-polly' | 'openai-cloud' | 'elevenlabs' | 'google-translate' | 'microsoft-edge' | 'streamlabs-polly';
18
18
  export type TimePitchShiftingMethod = 'sonic' | 'rubberband';
19
19
  export interface SynthesisOptions {
20
20
  engine?: SynthesisEngine;
@@ -50,8 +50,17 @@ export interface SynthesisOptions {
50
50
  speakerId?: number;
51
51
  provider?: OnnxExecutionProvider;
52
52
  };
53
+ kokoro?: {
54
+ provider?: OnnxExecutionProvider;
55
+ model?: '82m-v1.0-fp32' | '82m-v1.0-quantized';
56
+ };
53
57
  pico?: {};
54
58
  flite?: {};
59
+ gnuspeech?: {
60
+ tempo?: number;
61
+ controlRate?: number;
62
+ debug?: boolean;
63
+ };
55
64
  espeak?: {
56
65
  rate?: number;
57
66
  pitch?: number;
@@ -110,9 +110,9 @@ async function synthesizeSegments(segments, options, onSegment, onSentence) {
110
110
  const sentenceText = sentences[sentenceIndex].trim();
111
111
  logger.log(`\n${chalk.magentaBright(`Synthesizing sentence ${sentenceIndex + 1}/${sentences.length}`)}: "${sentenceText}"`);
112
112
  const sentenceStartTime = timeOffset;
113
- let sentencetSynthesisOptions = { postProcessing: { normalizeAudio: false } };
114
- sentencetSynthesisOptions = extendDeep(options, sentencetSynthesisOptions);
115
- const { synthesizedAudio: sentenceRawAudio, timeline: sentenceTimeline } = await synthesizeSegment(sentenceText, sentencetSynthesisOptions);
113
+ let sentenceSynthesisOptions = { postProcessing: { normalizeAudio: false } };
114
+ sentenceSynthesisOptions = extendDeep(options, sentenceSynthesisOptions);
115
+ const { synthesizedAudio: sentenceRawAudio, timeline: sentenceTimeline } = await synthesizeSegment(sentenceText, sentenceSynthesisOptions);
116
116
  const endPause = sentenceIndex == sentences.length - 1 ? options.segmentEndPause : options.sentenceEndPause;
117
117
  sentenceRawAudio.audioChannels[0] = trimAudioEnd(sentenceRawAudio.audioChannels[0], endPause * sentenceRawAudio.sampleRate);
118
118
  sentencesRawAudio.push(sentenceRawAudio);
@@ -284,6 +284,27 @@ async function synthesizeSegment(text, options) {
284
284
  logger.end();
285
285
  break;
286
286
  }
287
+ case 'kokoro': {
288
+ if (inputIsSSML) {
289
+ throw new Error(`The Kokoro engine doesn't currently support SSML inputs`);
290
+ }
291
+ const kokoroOptions = options.kokoro;
292
+ const kokoroTTS = await import('../synthesis/KokoroTTS.js');
293
+ const lexicons = await loadLexiconsForLanguage(language, options.customLexiconPaths);
294
+ const onnxExecutionProviders = kokoroOptions.provider ? [kokoroOptions.provider] : [];
295
+ const modelName = kokoroOptions.model;
296
+ const modelPackageName = `kokoro-${modelName}`;
297
+ const modelPath = await loadPackage(modelPackageName);
298
+ const voicesPath = await loadPackage('kokoro-82m-v1.0-voices');
299
+ logger.end();
300
+ logger.logTitledMessage(`Using model`, modelPackageName);
301
+ const { rawAudio, timeline: outTimeline } = await kokoroTTS.synthesizeSentence(text, selectedVoice, speed, lexicons, modelPath, voicesPath, onnxExecutionProviders);
302
+ synthesizedAudio = rawAudio;
303
+ timeline = outTimeline;
304
+ shouldPostprocessPitch = true;
305
+ logger.end();
306
+ break;
307
+ }
287
308
  case 'pico': {
288
309
  if (inputIsSSML) {
289
310
  throw new Error(`The SVOX Pico engine doesn't currently support SSML inputs`);
@@ -312,6 +333,24 @@ async function synthesizeSegment(text, options) {
312
333
  shouldPostprocessPitch = true;
313
334
  break;
314
335
  }
336
+ case 'gnuspeech': {
337
+ if (inputIsSSML) {
338
+ throw new Error(`The Gnuspeech engine doesn't currently support SSML inputs`);
339
+ }
340
+ const engineOptions = options.gnuspeech;
341
+ const GnuSpeech = await import('../synthesis/GnuSpeechTTS.js');
342
+ const { defaultGnuSpeechOptions } = await import('@echogarden/gnuspeech-wasm');
343
+ const gnuSpeechOptions = extendDeep(defaultGnuSpeechOptions, engineOptions);
344
+ if (!engineOptions.tempo) {
345
+ gnuSpeechOptions.tempo = speed;
346
+ }
347
+ await logger.startAsync(`Synthesize with Gnuspeech`);
348
+ const { rawAudio } = await GnuSpeech.synthesize(simplifiedText, gnuSpeechOptions);
349
+ synthesizedAudio = rawAudio;
350
+ shouldPostprocessPitch = true;
351
+ logger.end();
352
+ break;
353
+ }
315
354
  case 'espeak': {
316
355
  const EspeakTTS = await import('../synthesis/EspeakTTS.js');
317
356
  const engineOptions = options.espeak;
@@ -652,7 +691,7 @@ async function synthesizeSegment(text, options) {
652
691
  timeline = timeline.filter(entry => isWordOrSymbolWord(entry.text));
653
692
  }
654
693
  logger.end();
655
- logger.logDuration('Segment synthesis time', startTimestamp, chalk.magentaBright);
694
+ logger.logDuration('Part synthesis time', startTimestamp, chalk.magentaBright);
656
695
  return { synthesizedAudio, timeline };
657
696
  }
658
697
  function convertSpeedScaleToSSMLValueString(rate) {
@@ -728,13 +767,19 @@ export const defaultSynthesisOptions = {
728
767
  speakerId: undefined,
729
768
  provider: undefined,
730
769
  },
770
+ kokoro: {
771
+ model: '82m-v1.0-fp32'
772
+ },
731
773
  pico: {},
732
774
  flite: {},
775
+ gnuspeech: {
776
+ debug: false,
777
+ },
733
778
  espeak: {
734
779
  rate: undefined,
735
780
  pitch: undefined,
736
781
  pitchRange: undefined,
737
- useKlatt: false
782
+ useKlatt: false,
738
783
  },
739
784
  sam: {
740
785
  speed: undefined,
@@ -826,6 +871,11 @@ export async function requestVoiceList(options) {
826
871
  voiceList = SvoxPicoTTS.voiceList;
827
872
  break;
828
873
  }
874
+ case 'gnuspeech': {
875
+ const GnuSpeech = await import('../synthesis/GnuSpeechTTS.js');
876
+ voiceList = GnuSpeech.voiceList;
877
+ break;
878
+ }
829
879
  case 'sam': {
830
880
  voiceList.push({
831
881
  name: 'sam',
@@ -841,6 +891,11 @@ export async function requestVoiceList(options) {
841
891
  });
842
892
  break;
843
893
  }
894
+ case 'kokoro': {
895
+ const KokoroTTS = await import('../synthesis/KokoroTTS.js');
896
+ voiceList = KokoroTTS.voiceList;
897
+ break;
898
+ }
844
899
  case 'sapi': {
845
900
  const SapiTTS = await import('../synthesis/SapiTTS.js');
846
901
  await SapiTTS.AssertSAPIAvailable(false);
@@ -1066,28 +1121,40 @@ export const synthesisEngines = [
1066
1121
  description: 'A high-quality end-to-end neural speech synthesis architecture.',
1067
1122
  type: 'local'
1068
1123
  },
1124
+ {
1125
+ id: 'kokoro',
1126
+ name: 'Kokoro',
1127
+ description: 'A high-quality neural speech synthesis model based on the StyleTTS 2 architecture.',
1128
+ type: 'local'
1129
+ },
1069
1130
  {
1070
1131
  id: 'pico',
1071
1132
  name: 'SVOX Pico',
1072
- description: 'A legacy diphone-based synthesis engine.',
1133
+ description: 'A legacy diphone-based speech synthesizer.',
1073
1134
  type: 'local'
1074
1135
  },
1075
1136
  {
1076
1137
  id: 'flite',
1077
1138
  name: 'Flite',
1078
- description: 'A legacy diphone-based synthesis engine.',
1139
+ description: 'A legacy diphone-based speech synthesizer.',
1140
+ type: 'local'
1141
+ },
1142
+ {
1143
+ id: 'gnuspeech',
1144
+ name: 'Gnuspeech',
1145
+ description: 'A legacy articulatory speech synthesizer.',
1079
1146
  type: 'local'
1080
1147
  },
1081
1148
  {
1082
1149
  id: 'espeak',
1083
1150
  name: 'eSpeak NG',
1084
- description: `A lightweight 'robot' sounding formant-based synthesizer.`,
1151
+ description: `A lightweight, highly multilingual, 'robot'-like formant-based speech synthesizer.`,
1085
1152
  type: 'local'
1086
1153
  },
1087
1154
  {
1088
1155
  id: 'sam',
1089
1156
  name: 'SAM (Software Automatic Mouth)',
1090
- description: `A classic 'robot' speech synthesizer from 1982.`,
1157
+ description: `A classic 'robot'-like speech synthesizer from 1982.`,
1091
1158
  type: 'local'
1092
1159
  },
1093
1160
  {