echogarden 2.1.2 → 2.2.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 (50) hide show
  1. package/README.md +6 -1
  2. package/data/lexicons/heteronyms.en.json +31 -6
  3. package/data/lexicons/words.en.json +60 -0
  4. package/data/schemas/options.json +20 -4
  5. package/dist/api/Recognition.d.ts +3 -1
  6. package/dist/api/Recognition.js +17 -0
  7. package/dist/api/Recognition.js.map +1 -1
  8. package/dist/api/Synthesis.d.ts +2 -2
  9. package/dist/api/Synthesis.js +3 -3
  10. package/dist/api/Synthesis.js.map +1 -1
  11. package/dist/codecs/FFMpegTranscoder.js +2 -0
  12. package/dist/codecs/FFMpegTranscoder.js.map +1 -1
  13. package/dist/nlp/EspeakPhonemizer.d.ts +2 -2
  14. package/dist/nlp/EspeakPhonemizer.js +36 -36
  15. package/dist/nlp/PhoneConversion.d.ts +1 -0
  16. package/dist/nlp/PhoneConversion.js +151 -222
  17. package/dist/nlp/PhoneConversion.js.map +1 -1
  18. package/dist/recognition/DeepgramSTT.d.ts +11 -0
  19. package/dist/recognition/DeepgramSTT.js +64 -0
  20. package/dist/recognition/DeepgramSTT.js.map +1 -0
  21. package/dist/recognition/GoogleCloudSTT.js.map +1 -1
  22. package/dist/synthesis/{ElevenlabsTTS.d.ts → ElevenLabsTTS.d.ts} +3 -3
  23. package/dist/synthesis/{ElevenlabsTTS.js → ElevenLabsTTS.js} +3 -3
  24. package/dist/synthesis/{ElevenlabsTTS.js.map → ElevenLabsTTS.js.map} +1 -1
  25. package/dist/synthesis/EspeakTTS.d.ts +1 -1
  26. package/dist/synthesis/EspeakTTS.js +11 -7
  27. package/dist/synthesis/EspeakTTS.js.map +1 -1
  28. package/dist/synthesis/KokoroTTS.js +14 -14
  29. package/dist/synthesis/VitsTTS.js +9 -9
  30. package/dist/utilities/Utilities.d.ts +1 -0
  31. package/dist/utilities/Utilities.js +5 -0
  32. package/dist/utilities/Utilities.js.map +1 -1
  33. package/dist/utilities/WasmMemoryManager.d.ts +1 -1
  34. package/docs/CUDA.md +6 -6
  35. package/docs/Development.md +1 -1
  36. package/docs/Engines.md +1 -0
  37. package/docs/Options.md +5 -1
  38. package/package.json +6 -6
  39. package/src/api/Recognition.ts +29 -1
  40. package/src/api/Synthesis.ts +7 -7
  41. package/src/codecs/FFMpegTranscoder.ts +2 -0
  42. package/src/nlp/EspeakPhonemizer.ts +36 -36
  43. package/src/nlp/PhoneConversion.ts +176 -225
  44. package/src/recognition/DeepgramSTT.ts +136 -0
  45. package/src/recognition/GoogleCloudSTT.ts +0 -1
  46. package/src/synthesis/{ElevenlabsTTS.ts → ElevenLabsTTS.ts} +4 -4
  47. package/src/synthesis/EspeakTTS.ts +12 -7
  48. package/src/synthesis/KokoroTTS.ts +15 -15
  49. package/src/synthesis/VitsTTS.ts +9 -9
  50. package/src/utilities/Utilities.ts +6 -0
@@ -5,7 +5,7 @@ export function ipaPhoneToKirshenbaum(ipaPhone: string) {
5
5
  const convertedChar = ipaToKirshenbaum[char]
6
6
 
7
7
  if (convertedChar == undefined) {
8
- throw new Error(`Could not convert phone character '${char}' to Kirshenbaum encoding`)
8
+ throw new Error(`Couldn't convert IPA character '${char}' (part of phone '${ipaPhone}' the Kirshenbaum notation`)
9
9
  }
10
10
 
11
11
  result += convertedChar || '_'
@@ -252,257 +252,208 @@ const ipaToTimit: { [p: string]: string[] | undefined } = {
252
252
  'ɾ': ['dx'],
253
253
  }
254
254
 
255
- // This is adapted from a lookup table on the eSpeak-ng source code
256
- const ipaToKirshenbaum: { [p: string]: string | undefined } = {
257
- '1': '1',
258
- '2': '2',
259
- '4': '4',
260
- '5': '5',
261
- '6': '6',
262
- '7': '7',
263
- '9': '9',
264
- ' ': ' ',
265
- '!': '!',
266
- '\'': '\'',
267
- 'ʰ': '#',
268
- '$': '$',
269
- '%': '%',
270
- //'æ': '&',
271
- 'æ': 'a',
272
- 'ˈ': '\'',
273
- '(': '(',
274
- ')': ')',
275
- 'ɾ': '*',
276
- '+': '+',
277
- 'ˌ': ',',
278
- '-': '-',
279
- '.': '.',
280
- '/': '/',
281
- 'ɒ': '0',
282
- 'ɜ': '3',
283
- 'ɵ': '8',
284
- 'ː': ':',
285
- 'ʲ': ';',
286
- '<': '<',
287
- '=': '=',
288
- '>': '>',
289
- 'ʔ': '?',
290
- 'ə': '@',
291
- 'ɑ': 'A',
292
- 'β': 'B',
293
- 'ç': 'C',
294
- 'ð': 'D',
295
- 'ɛ': 'E',
296
- 'F': 'F',
297
- 'ɢ': 'G',
298
- 'ħ': 'H',
299
- 'ɪ': 'I',
300
- 'ɟ': 'J',
301
- 'K': 'K',
302
- 'ɫ': 'L',
303
- 'ɱ': 'M',
304
- 'ŋ': 'N',
305
- 'ɔ': 'O',
306
- 'Φ': 'P',
307
- 'ɣ': 'Q',
308
- 'ʀ': 'R',
309
- 'ʃ': 'S',
310
- 'θ': 'T',
311
- 'ʊ': 'U',
312
- 'ʌ': 'V',
313
- 'œ': 'W',
314
- 'χ': 'X',
315
- 'ø': 'Y',
316
- 'ʒ': 'Z',
317
- '̪': '[',
318
- '\\': '\\',
319
- ']': ']',
320
- '^': '^',
321
- '_': '_',
322
- '`': '`',
323
- 'a': 'a',
324
- 'b': 'b',
325
- 'c': 'c',
326
- 'd': 'd',
327
- 'e': 'e',
328
- 'f': 'f',
329
- 'ɡ': 'g',
330
- 'h': 'h',
331
- 'i': 'i',
332
- 'j': 'j',
333
- 'k': 'k',
334
- 'l': 'l',
335
- 'm': 'm',
336
- 'n': 'n',
337
- 'o': 'o',
338
- 'p': 'p',
339
- 'q': 'q',
340
- 'r': 'r',
341
- 's': 's',
342
- 't': 't',
343
- 'u': 'u',
344
- 'v': 'v',
345
- 'w': 'w',
346
- 'x': 'x',
347
- 'y': 'y',
348
- 'z': 'z',
349
- '{': '{',
350
- '|': '|',
351
- '}': '}',
352
- '̃': '~',
353
- '': '',
354
-
355
- // Extensions
356
- 'ɚ': '3',
357
- 'ɹ': 'r',
255
+ export const ipaToKirshenbaum: Record<string, string> = {
256
+ //// Vowels
257
+
258
+ // Close
259
+ 'i': 'i',
260
+ 'y': 'y',
261
+ 'ɨ': 'i"',
262
+ 'ʉ': 'u"',
263
+ 'ɯ': 'u-',
264
+ 'u': 'u',
265
+
266
+ // Near-close
267
+ 'ɪ': 'I',
268
+ 'ɩ': 'I',
269
+ '': 'I',
270
+ 'ʏ': 'I.',
271
+ 'ʊ': 'U',
272
+ 'ɷ': 'U',
273
+
274
+ // Close-mid
275
+ 'e': 'e',
276
+ 'ø': 'Y',
277
+ //'ɘ': '@<umd>', // Reference
278
+ 'ɘ': '@', // Simplified
279
+ 'ɵ': '@.',
280
+ //'ɵ': '8', // eSpeak
281
+ 'ɤ': 'o-',
282
+ 'o': 'o',
283
+
284
+ // Mid:
285
+ 'ə': '@',
286
+
287
+ // Open-mid:
288
+ 'ɛ': 'E',
289
+ 'œ': 'W',
290
+ //'ɜ': '3', // eSpeak
291
+ 'ɜ': 'V"',
292
+ 'ɞ': 'O"',
293
+ 'ʌ': 'V',
294
+ 'ɔ': 'O',
295
+
296
+ // Near-open
297
+ //'æ': '&', // Reference. note: passing '&' to eSpeak in SSML (XML) mode will cause an error due to escaping conflict
298
+ 'æ': 'a', // eSpeak
358
299
  'ɐ': 'a#',
359
- 'ᵻ': 'i',
360
- '̩': ','
361
- }
362
300
 
363
- /*
364
- // Source: https://github.com/coruus/ascii-ipa/blob/master/kirshenbaum.py
365
- // Conversion regex: \('.*?': '.*?'\)
366
- // Replace: $2: $1
367
- const ipaToKirshenbaum2: { [p: string]: string | undefined } = {
301
+ // Open
302
+ 'a': 'a',
303
+ //'ɶ': '&.', // Reference. note: passing '&' to eSpeak in SSML (XML) mode will cause an error due to escaping conflict
304
+ 'ɶ': 'W#', // eSpeak
305
+ 'ä': 'a"',
306
+ 'ɒ̈': 'A".',
307
+ 'ɑ': 'A',
308
+ //'ɒ': 'A.', // Reference
309
+ 'ɒ': '0', // eSpeak
310
+
311
+ // Rhotic
312
+ 'ɚ': 'R',
313
+ //'ɚ': '3', // eSpeak
314
+ //'ɝ': 'R<umd>', // Reference
315
+ 'ɝ': 'R', // Simplified
316
+
317
+ // More vowels
318
+ 'ᵊ': '',
319
+
320
+ //// Consonants
321
+
322
+ // Nasals
368
323
  'm': 'm',
324
+ 'ɱ': 'M',
325
+ 'n̪': 'n[',
326
+ 'n': 'n',
327
+ 'ɳ': 'n.',
328
+ 'ṇ': 'n.',
329
+ 'ɲ': 'n^',
330
+ 'ŋ': 'N',
331
+ 'ɴ': 'n"',
332
+ //'n͡g': 'n<lbv>',
333
+
334
+ // Stops
369
335
  'p': 'p',
370
336
  'b': 'b',
337
+ 't̪': 't[',
338
+ 'd̪': 'd[',
339
+ 't': 't',
340
+ 'd': 'd',
341
+ 'ʈ': 't.',
342
+ 'ṭ': 't.',
343
+ 'ɖ': 'd.',
344
+ 'ḍ': 'd.',
345
+ 'c': 'c',
346
+ 'ɟ': 'J',
347
+ 'k': 'k',
348
+ 'ɡ': 'g',
349
+ 'q': 'q',
350
+ 'ɢ': 'G',
351
+ //'k͡p': 't<lbv>',
352
+ //'ɡ͡b': 'd<lbv>',
353
+ 'ʔ': '?',
354
+
355
+ // Fricatives
356
+ 'φ': 'P',
371
357
  'Φ': 'P',
372
358
  'β': 'B',
373
- 'ʙ': 'b<trl>',
374
- 'pʼ': 'p`',
375
- 'ɓ': 'b`',
376
- 'ʘ': 'p!',
377
- 'ɱ': 'M',
378
359
  'f': 'f',
379
360
  'v': 'v',
380
- 'ʋ': 'r<lbd>',
381
- 'n\u032a': 'n[',
382
- 't\u032a': 't[',
383
361
  'θ': 'T',
384
362
  'ð': 'D',
385
- 'r\u032a': 'r[',
386
- 'l\u032a': 'l[',
387
- 't\u032a\u02bc': 't[`',
388
- 'ɗ': 'd`',
389
- 'ʇ': 't!',
390
- 'n': 'n',
391
- 't': 't',
392
- 'd': 'd',
393
363
  's': 's',
394
364
  'z': 'z',
395
- 'ɬ': 's<lat>',
396
- 'ɮ': 'z<lat>',
397
- 'ɹ': 'r',
398
- 'l': 'l',
399
- 'ʀ': 'r<trl>',
400
- 'ɾ': '*',
401
- 'ɺ': '*<lat>',
402
- 't\u02bc': 't`',
403
- 'ʗ': 'c!',
404
- 'ʖ': 'l!',
405
- 'ɳ': 'n.',
406
- 'ʈ': 't.',
407
- 'ɖ': 'd.',
408
365
  'ʂ': 's.',
366
+ 'ṣ': 's.',
409
367
  'ʐ': 'z.',
410
- //'ɖ': 'r.',
411
- 'ɭ': 'l.',
412
- 'ɽ': '*.',
368
+ '': 'z.',
413
369
  'ʃ': 'S',
414
370
  'ʒ': 'Z',
415
- 'n^': 'n^',
416
- 'c': 'c',
417
- 'ɟ': 'J',
418
371
  'ç': 'C',
419
372
  'ʝ': 'C<vcd>',
420
- 'j': 'j',
421
- 'ɥ': 'j<rnd>',
422
- 'ʎ': 'l^',
423
- 'ʄ': 'J`',
424
- 'ŋ': 'N',
425
- 'k': 'k',
426
- 'g': 'g',
427
373
  'x': 'x',
428
374
  'ɣ': 'Q',
429
- 'ɰ': 'j<vel>',
430
- 'ɫ': 'L',
431
- //'ɬ': '{vls,alv,lat,frc}',
432
- 'k\u02bc': 'k`',
433
- 'g\u02bc': 'g`',
434
- 'ʞ': 'k!',
435
- 'n\u2030g': 'n<lbv>',
436
- 'k\u2030p': 't<lbv>',
437
- 'g\u2030b': 'n<lbv>',
375
+ 'χ': 'X',
376
+ 'ʁ': 'g"', // Reference
377
+ //'ʁ': 'r', // eSpeak
438
378
  'ʍ': 'w<vls>',
439
379
  'w': 'w',
440
- 'ɴ': 'n'',
441
- 'q': 'q',
442
- 'ɢ': 'G',
443
- 'χ': 'X',
444
- 'ʁ': 'g'',
445
- //'ʀ': 'r'',
446
- 'ʠ': 'q`',
447
- 'ʛ': 'G`',
448
380
  'ħ': 'H',
449
381
  'ʕ': 'H<vcd>',
450
- 'ʔ': '?',
451
382
  'h': 'h',
452
383
  'ɦ': 'h<?>',
453
- 'i': 'i',
454
- 'y': 'y',
455
- 'ɪ': 'I',
456
- 'ʏ': 'I.',
457
- 'e': 'e',
458
- 'ø': 'Y',
459
- 'ɛ': 'E',
460
- 'œ': 'W',
461
- 'æ': '&',
462
- 'ɶ': '&.',
463
- 'ɨ': 'i'',
464
- 'ʉ': 'u'',
465
- 'ɘ': '@<umd>',
466
- 'ɝ': 'R<umd>',
467
- 'ə': '@',
468
- 'ɚ': 'R',
469
- 'ɵ': '@.',
470
- //'ɜ': 'V'',
471
- 'ɜ': '3',
472
- 'ɞ': 'O'',
473
- 'a': 'a',
474
- 'ɯ': 'u-',
475
- 'u': 'u',
476
- 'ʊ': 'U',
477
- 'ɤ': 'o-',
478
- 'o': 'o',
479
- 'ʌ': 'V',
480
- 'ɔ': 'O',
481
- 'ɑ': 'A',
482
- 'ɒ': 'A.',
483
384
 
484
- 'ː': ':',
485
- '\u0322': '.',
486
- '\u02bc': '`',
487
- '\u032a': '[',
488
- '\u02b2': ';',
489
- ''': ''',
490
- '^': '^',
491
- '\u0334': '<H>',
492
- '\u02b0': '<h>',
493
- '\u02da': '<unx>',
494
- '\u0325': '<vls>',
495
- //'\u02da': '<o>',
496
- '\u02b3': '<r>',
497
- '\u02b7': '<w>',
498
- '\u02b1': '<?>',
499
-
500
- '\u0303': '~',
501
- //'\u0334': '~'
502
-
503
- 'ˈ': ''',
385
+ // Approximants
386
+ 'ʋ': 'r<lbd>',
387
+ 'ɹ̪': 'r[',
388
+ 'ɹ': 'r',
389
+ 'ɻ': 'r.',
390
+ 'ɻ̣': 'r.',
391
+ 'j': 'j',
392
+ 'ɥ': 'j<rnd>',
393
+ 'ɰ': 'j<vel>',
394
+
395
+ // Laterals
396
+ '': 'l[',
397
+ 'l': 'l',
398
+ 'ɭ': 'l.',
399
+ '': 'l.',
400
+ 'ʎ': 'l^',
401
+ 'ʟ': 'L',
402
+
403
+ // Trills
404
+ 'ʙ': 'b<trl>',
405
+ //'r': 'r<trl>', // Reference alv trl
406
+ 'r': 'R', // eSpeak
407
+ 'ʀ': 'r"', // Reference uvl trl
408
+
409
+ // Flaps
410
+ 'ɾ': '*',
411
+ 'ɽ': '*.',
412
+ 'ṛ': '*.',
413
+
414
+ // Ejectives
415
+ 'pʼ': 'p`',
416
+ 'tʼ': 't`',
417
+ 'cʼ': 'c`',
418
+ 'kʼ': 'k`',
419
+ 'qʼ': 'q`',
420
+ 'ʠ': 'q`',
421
+
422
+ // Implosives
423
+ 'ɓ': 'b`',
424
+ 'ɗ': 'd`',
425
+ 'ʄ': 'J`',
426
+ 'ɠ': 'g`',
427
+ 'ʛ': 'G`',
428
+
429
+ // Clicks
430
+ 'ʘ': 'p!',
431
+ 'ǀ': 't!',
432
+ 'ʇ': 't!',
433
+ 'ǃ': 'c!',
434
+ 'ǂ': 'c!',
435
+ 'ʗ': 'c!',
436
+ 'ʞ': 'k!',
437
+ 'ǁ': 'l!',
438
+ 'ʖ': 'l!',
439
+
440
+ // Lateral fricative
441
+ 'ɬ': 'L',
442
+ 'ɮ': 'z<lat>',
443
+ 'ɫ': 'L',
444
+
445
+ // Lateral flap
446
+ 'ɺ': '*<lat>',
447
+
448
+ // Other consonants
449
+ 'ʰ': '#',
450
+ 'ʲ': ';',
451
+ '̪': '[',
452
+ '̃': '~',
453
+
454
+ // Stress marks and other misc characters
455
+ 'ˈ': '\'',
504
456
  'ˌ': ',',
505
- ' ': ' ',
506
- '\n': '\n'
457
+ 'ː': ':',
458
+ '-': '-',
507
459
  }
508
- */
@@ -0,0 +1,136 @@
1
+ import { request } from 'gaxios'
2
+ import { RawAudio } from '../audio/AudioUtilities.js'
3
+ import { Logger } from '../utilities/Logger.js'
4
+ import { extendDeep } from '../utilities/ObjectUtilities.js'
5
+ import { Timeline, TimelineEntry } from '../utilities/Timeline.js'
6
+ import * as FFMpegTranscoder from '../codecs/FFMpegTranscoder.js'
7
+
8
+ export async function recognize(rawAudio: RawAudio, languageCode: string | undefined, options: DeepgramSTTOptions) {
9
+ const logger = new Logger()
10
+
11
+ logger.start('Initialize Deepgram recognition')
12
+
13
+ options = extendDeep(defaultDeepgramSTTOptions, options)
14
+
15
+ if (!options.apiKey) {
16
+ throw new Error('No Deepgram API key provided')
17
+ }
18
+
19
+ // Prepare API request
20
+ const params: Record<string, string> = {
21
+ model: options.model || 'whisper-large',
22
+ encoding: 'flac',
23
+ //punctuate: 'true', // Problem when enabled: words in timeline are not capitalized, causing errors
24
+ }
25
+
26
+ // Set language or enable auto-detection
27
+ if (languageCode) {
28
+ params.language = languageCode
29
+ } else {
30
+ params.detect_language = 'true'
31
+ }
32
+
33
+ // Set audio encoding parameters
34
+ logger.start('Convert audio to FLAC format')
35
+
36
+ const audioData = await FFMpegTranscoder.encodeFromChannels(
37
+ rawAudio,
38
+ FFMpegTranscoder.getDefaultFFMpegOptionsForSpeech('flac')
39
+ )
40
+
41
+ logger.start('Send request to Deepgram API')
42
+
43
+ const response = await request<any>({
44
+ method: 'POST',
45
+
46
+ url: 'https://api.deepgram.com/v1/listen',
47
+
48
+ params,
49
+
50
+ headers: {
51
+ 'Authorization': `Token ${options.apiKey}`,
52
+ 'Content-Type': 'audio/flac',
53
+ 'Accept': 'application/json'
54
+ },
55
+
56
+ body: audioData,
57
+
58
+ responseType: 'json',
59
+ })
60
+
61
+ const deepgramResponse: DeepgramResponse = response.data
62
+
63
+ const firstAlternative = deepgramResponse.results?.channels[0]?.alternatives[0]
64
+
65
+ // Extract transcript and create timeline
66
+ const transcript = firstAlternative?.transcript || ''
67
+
68
+ let timeline: Timeline = []
69
+
70
+ // Extract word-level timing information if available
71
+ const words = firstAlternative?.words || []
72
+
73
+ if (words.length > 0) {
74
+ timeline = words.map((word: DeepgramWordEntry): TimelineEntry => ({
75
+ type: 'word',
76
+ text: word.word,
77
+ startTime: word.start,
78
+ endTime: word.end,
79
+ confidence: word.confidence,
80
+ }))
81
+ }
82
+
83
+ logger.end()
84
+
85
+ return { transcript, timeline }
86
+ }
87
+
88
+ export interface DeepgramSTTOptions {
89
+ apiKey?: string
90
+ model?: string
91
+ }
92
+
93
+ export const defaultDeepgramSTTOptions: DeepgramSTTOptions = {
94
+ apiKey: undefined,
95
+ model: 'nova-2'
96
+ }
97
+
98
+ interface DeepgramWordEntry {
99
+ word: string
100
+ start: number
101
+ end: number
102
+ confidence: number
103
+ }
104
+
105
+ interface DeepgramAlternative {
106
+ transcript: string
107
+ confidence: number
108
+ words: DeepgramWordEntry[]
109
+ }
110
+
111
+ interface DeepgramChannel {
112
+ alternatives: DeepgramAlternative[]
113
+ }
114
+
115
+ interface DeepgramResponse {
116
+ metadata?: {
117
+ transaction_key: string
118
+ request_id: string
119
+ sha256: string
120
+ created: string
121
+ duration: number
122
+ channels: number
123
+ models: string[]
124
+
125
+ model_info?: {
126
+ name: string
127
+ version: string
128
+ }
129
+ }
130
+
131
+ results?: {
132
+ channels: DeepgramChannel[]
133
+
134
+ detected_language?: string
135
+ }
136
+ }
@@ -50,7 +50,6 @@ export async function recognize(rawAudio: RawAudio, apiKey: string, languageCode
50
50
 
51
51
  data: requestBody,
52
52
 
53
-
54
53
  responseType: 'json'
55
54
  })
56
55
 
@@ -7,11 +7,11 @@ import { extendDeep } from '../utilities/ObjectUtilities.js'
7
7
 
8
8
  const log = logToStderr
9
9
 
10
- export async function synthesize(text: string, voiceId: string, modelId: string, options: ElevenlabsTTSOptions) {
10
+ export async function synthesize(text: string, voiceId: string, modelId: string, options: ElevenLabsTTSOptions) {
11
11
  const logger = new Logger()
12
12
  logger.start('Request synthesis from ElevenLabs')
13
13
 
14
- options = extendDeep(defaultElevenlabsTTSOptions, options)
14
+ options = extendDeep(defaultElevenLabsTTSOptions, options)
15
15
 
16
16
  let response: GaxiosResponse<any>
17
17
 
@@ -118,7 +118,7 @@ export async function getVoiceList(apiKey: string) {
118
118
  return voices
119
119
  }
120
120
 
121
- export interface ElevenlabsTTSOptions {
121
+ export interface ElevenLabsTTSOptions {
122
122
  apiKey?: string
123
123
  stability?: number
124
124
  similarityBoost?: number
@@ -126,7 +126,7 @@ export interface ElevenlabsTTSOptions {
126
126
  useSpeakerBoost?: boolean
127
127
  }
128
128
 
129
- export const defaultElevenlabsTTSOptions = {
129
+ export const defaultElevenLabsTTSOptions = {
130
130
  apiKey: undefined,
131
131
  stability: 0.5,
132
132
  similarityBoost: 0.5,
@@ -169,9 +169,12 @@ export async function synthesizeFragments(fragments: string[], espeakOptions: Es
169
169
 
170
170
  fragment = simplifyPunctuationCharacters(fragment)
171
171
 
172
- fragment = fragment
173
- .replaceAll('<', '&lt;')
174
- .replaceAll('>', '&gt;')
172
+ //fragment = encodeHTMLAngleBrackets(fragment)
173
+ fragment = fragment.replaceAll('<', '_').replaceAll('>', '_')
174
+
175
+ if (fragment.split('').every(c => c === ':')) {
176
+ fragment = ','
177
+ }
175
178
 
176
179
  if (espeakOptions.insertSeparators && canInsertSeparators) {
177
180
  const separator = ` | `
@@ -597,9 +600,9 @@ export const defaultEspeakOptions: EspeakOptions = {
597
600
  insertSeparators: false
598
601
  }
599
602
 
600
- export async function testKirshenbaumPhonemization(text: string) {
601
- const ipaPhonemizedSentence = (await phonemizeSentence(text, 'en-us')).flatMap(clause => clause)
602
- const kirshenbaumPhonemizedSentence = (await phonemizeSentence(text, 'en-us', undefined, false)).flatMap(clause => clause)
603
+ export async function testKirshenbaumPhonemization(text: string, language = 'en-us') {
604
+ const ipaPhonemizedSentence = (await phonemizeSentence(text, language)).flatMap(clause => clause)
605
+ const kirshenbaumPhonemizedSentence = (await phonemizeSentence(text, language, undefined, false)).flatMap(clause => clause)
603
606
 
604
607
  const ipaFragments = ipaPhonemizedSentence.map(word => word.join(''))
605
608
 
@@ -610,6 +613,8 @@ export async function testKirshenbaumPhonemization(text: string) {
610
613
  ipaPhoneToKirshenbaum(phoneme)).join(''))
611
614
 
612
615
  for (let i = 0; i < fragments.length; i++) {
613
- log(`IPA: ${ipaFragments[i]} | converted: ${fragments[i]} | ground truth: ${kirshenbaumFragments[i]}`)
616
+ if (fragments[i] !== kirshenbaumFragments[i]) {
617
+ log(`IPA: ${ipaFragments[i]} | converted: ${fragments[i]} | ground truth: ${kirshenbaumFragments[i]}`)
618
+ }
614
619
  }
615
620
  }