echogarden 3.0.5 → 3.2.0

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 (107) hide show
  1. package/data/lexicons/heteronyms.en.json +2 -2
  2. package/data/lexicons/words.en.json +120 -0
  3. package/dist/alignment/SemanticTextAlignment.js +2 -2
  4. package/dist/alignment/SemanticTextAlignment.js.map +1 -1
  5. package/dist/api/Alignment.js +2 -2
  6. package/dist/api/Alignment.js.map +1 -1
  7. package/dist/api/Synthesis.js +2 -2
  8. package/dist/api/Synthesis.js.map +1 -1
  9. package/dist/encodings/Ascii.d.ts +1 -3
  10. package/dist/encodings/Ascii.d.ts.map +1 -1
  11. package/dist/encodings/Ascii.js +13 -9
  12. package/dist/encodings/Ascii.js.map +1 -1
  13. package/dist/encodings/Base64.d.ts.map +1 -1
  14. package/dist/encodings/Base64.js +9 -0
  15. package/dist/encodings/Base64.js.map +1 -1
  16. package/dist/encodings/Hex.d.ts.map +1 -1
  17. package/dist/encodings/Hex.js +11 -2
  18. package/dist/encodings/Hex.js.map +1 -1
  19. package/dist/encodings/LEB128.d.ts +1 -15
  20. package/dist/encodings/LEB128.d.ts.map +1 -1
  21. package/dist/encodings/LEB128.js +1 -247
  22. package/dist/encodings/LEB128.js.map +1 -1
  23. package/dist/encodings/Utf16.d.ts +1 -3
  24. package/dist/encodings/Utf16.d.ts.map +1 -1
  25. package/dist/encodings/Utf16.js +13 -9
  26. package/dist/encodings/Utf16.js.map +1 -1
  27. package/dist/encodings/Utf32.d.ts.map +1 -1
  28. package/dist/encodings/Utf32.js +6 -0
  29. package/dist/encodings/Utf32.js.map +1 -1
  30. package/dist/encodings/Utf8.d.ts +9 -5
  31. package/dist/encodings/Utf8.d.ts.map +1 -1
  32. package/dist/encodings/Utf8.js +36 -78
  33. package/dist/encodings/Utf8.js.map +1 -1
  34. package/dist/nlp/Segmentation.d.ts +2 -8
  35. package/dist/nlp/Segmentation.d.ts.map +1 -1
  36. package/dist/nlp/Segmentation.js +42 -19
  37. package/dist/nlp/Segmentation.js.map +1 -1
  38. package/dist/nlp/TextNormalizer.d.ts.map +1 -1
  39. package/dist/nlp/TextNormalizer.js +29 -14
  40. package/dist/nlp/TextNormalizer.js.map +1 -1
  41. package/dist/recognition/AmazonTranscribeSTT.js +3 -3
  42. package/dist/recognition/AmazonTranscribeSTT.js.map +1 -1
  43. package/dist/synthesis/AzureCognitiveServicesTTS.js +1 -1
  44. package/dist/synthesis/AzureCognitiveServicesTTS.js.map +1 -1
  45. package/dist/synthesis/EspeakTTS.d.ts.map +1 -1
  46. package/dist/synthesis/EspeakTTS.js +15 -8
  47. package/dist/synthesis/EspeakTTS.js.map +1 -1
  48. package/dist/synthesis/KokoroTTS.d.ts.map +1 -1
  49. package/dist/synthesis/KokoroTTS.js +0 -5
  50. package/dist/synthesis/KokoroTTS.js.map +1 -1
  51. package/dist/synthesis/MicrosoftEdgeTTS.js +1 -1
  52. package/dist/synthesis/MicrosoftEdgeTTS.js.map +1 -1
  53. package/dist/synthesis/VitsTTS.d.ts.map +1 -1
  54. package/dist/synthesis/VitsTTS.js +0 -5
  55. package/dist/synthesis/VitsTTS.js.map +1 -1
  56. package/dist/utilities/FileSystem.d.ts +12 -7
  57. package/dist/utilities/FileSystem.d.ts.map +1 -1
  58. package/dist/utilities/FileSystem.js +196 -79
  59. package/dist/utilities/FileSystem.js.map +1 -1
  60. package/dist/utilities/FileWriter.d.ts +1 -0
  61. package/dist/utilities/FileWriter.d.ts.map +1 -1
  62. package/dist/utilities/FileWriter.js +8 -1
  63. package/dist/utilities/FileWriter.js.map +1 -1
  64. package/dist/utilities/ObjectUtilities.d.ts.map +1 -1
  65. package/dist/utilities/ObjectUtilities.js +15 -13
  66. package/dist/utilities/ObjectUtilities.js.map +1 -1
  67. package/dist/utilities/PackageManager.js +2 -2
  68. package/dist/utilities/PackageManager.js.map +1 -1
  69. package/dist/utilities/PathUtilities.d.ts +1 -0
  70. package/dist/utilities/PathUtilities.d.ts.map +1 -1
  71. package/dist/utilities/PathUtilities.js +18 -0
  72. package/dist/utilities/PathUtilities.js.map +1 -1
  73. package/dist/utilities/StringUtilities.d.ts.map +1 -1
  74. package/dist/utilities/StringUtilities.js.map +1 -1
  75. package/dist/utilities/Timeline.js +2 -2
  76. package/dist/utilities/Timeline.js.map +1 -1
  77. package/dist/utilities/WikipediaReader.js +5 -5
  78. package/dist/utilities/WikipediaReader.js.map +1 -1
  79. package/package.json +13 -12
  80. package/src/alignment/SemanticTextAlignment.ts +2 -2
  81. package/src/api/Alignment.ts +2 -2
  82. package/src/api/Synthesis.ts +2 -2
  83. package/src/encodings/Ascii.ts +14 -10
  84. package/src/encodings/Base64.ts +9 -0
  85. package/src/encodings/Hex.ts +11 -2
  86. package/src/encodings/Utf16.ts +14 -10
  87. package/src/encodings/Utf32.ts +6 -0
  88. package/src/encodings/Utf8.ts +45 -82
  89. package/src/nlp/Segmentation.ts +53 -19
  90. package/src/nlp/TextNormalizer.ts +40 -16
  91. package/src/recognition/AmazonTranscribeSTT.ts +3 -3
  92. package/src/synthesis/AzureCognitiveServicesTTS.ts +1 -1
  93. package/src/synthesis/EspeakTTS.ts +17 -9
  94. package/src/synthesis/KokoroTTS.ts +0 -6
  95. package/src/synthesis/MicrosoftEdgeTTS.ts +1 -1
  96. package/src/synthesis/VitsTTS.ts +0 -6
  97. package/src/utilities/FileSystem.ts +224 -85
  98. package/src/utilities/FileWriter.ts +11 -1
  99. package/src/utilities/ObjectUtilities.ts +19 -15
  100. package/src/utilities/PackageManager.ts +2 -2
  101. package/src/utilities/PathUtilities.ts +19 -0
  102. package/src/utilities/StringUtilities.ts +0 -2
  103. package/src/utilities/Timeline.ts +2 -2
  104. package/src/utilities/WikipediaReader.ts +5 -5
  105. package/src/encodings/HtmlEscape.ts +0 -39
  106. package/src/encodings/LEB128.ts +0 -362
  107. package/src/utilities/StringBuilder.ts +0 -45
@@ -1,5 +1,8 @@
1
- import { EncodeIntoResult } from "./TextEncodingsCommon.js"
1
+ import { EncodeIntoResult } from './TextEncodingsCommon.js'
2
2
 
3
+ //////////////////////////////////////////////////////////////////////////////
4
+ // UTF-8 Encoding
5
+ //////////////////////////////////////////////////////////////////////////////
3
6
  export function encodeUtf8(text: string) {
4
7
  const textEncoder = new TextEncoder()
5
8
 
@@ -14,104 +17,64 @@ export function encodeUtf8Into(text: string, outputArray: Uint8Array): EncodeInt
14
17
  return result
15
18
  }
16
19
 
17
- export function decodeUtf8(encodedString: Uint8Array) {
18
- const maxChunkLength = 2 ** 24
20
+ export class ChunkedUtf8Encoder {
21
+ private readonly textEncoder = new TextEncoder()
19
22
 
20
- const decoder = new ChunkedUtf8Decoder()
23
+ private pendingHighSurrogate = ''
21
24
 
22
- for (let offset = 0; offset < encodedString.length; offset += maxChunkLength) {
23
- const chunk = encodedString.subarray(offset, offset + maxChunkLength)
25
+ writeChunk(stringChunk: string): Uint8Array {
26
+ if (this.pendingHighSurrogate !== '') {
27
+ stringChunk = this.pendingHighSurrogate + stringChunk
24
28
 
25
- decoder.writeChunk(chunk)
26
- }
29
+ this.pendingHighSurrogate = ''
30
+ }
27
31
 
28
- return decoder.toString()
29
- }
32
+ const lastCodeUnit = stringChunk.charCodeAt(stringChunk.length - 1)
30
33
 
31
- export class ChunkedUtf8Decoder {
32
- private str = ''
33
- private readonly textDecoder = new TextDecoder('utf-8')
34
+ if (lastCodeUnit >= 0xD800 && lastCodeUnit <= 0xDBFF) {
35
+ this.pendingHighSurrogate = stringChunk[stringChunk.length - 1]
34
36
 
35
- writeChunk(chunk: Uint8Array) {
36
- const decodedChunk = this.textDecoder.decode(chunk, { stream: true })
37
+ stringChunk = stringChunk.substring(0, stringChunk.length - 1)
38
+ }
37
39
 
38
- this.str += decodedChunk
40
+ return this.textEncoder.encode(stringChunk)
39
41
  }
40
42
 
41
- toString() {
42
- return this.str
43
+ finalize(): Uint8Array {
44
+ const result = this.textEncoder.encode(this.pendingHighSurrogate)
45
+
46
+ this.pendingHighSurrogate = ''
47
+
48
+ return result
43
49
  }
44
50
  }
45
51
 
46
- //////////////////////////////////////////////////////////////////////////////////////////////
47
- // Pure JavaScript implementations
48
- //////////////////////////////////////////////////////////////////////////////////////////////
49
- function encodeUtf8Into_JS(str: string, outputArray: Uint8Array): EncodeIntoResult {
50
- let readOffset = 0
51
- let writeOffset = 0
52
-
53
- while (readOffset < str.length) {
54
- const charCode = str.codePointAt(readOffset++)!
55
-
56
- if (charCode <= 0x7f) {
57
- outputArray[writeOffset++] = charCode
58
- } else if (charCode <= 0x7ff) {
59
- outputArray[writeOffset++] = 0xc0 | (charCode >>> 6)
60
- outputArray[writeOffset++] = 0x80 | (charCode & 63)
61
- } else if (charCode <= 0xffff) {
62
- outputArray[writeOffset++] = 0xe0 | (charCode >>> 12)
63
- outputArray[writeOffset++] = 0x80 | ((charCode >>> 6) & 63)
64
- outputArray[writeOffset++] = 0x80 | (charCode & 63)
65
- } else if (charCode <= 0x10ffff) {
66
- outputArray[writeOffset++] = 0xf0 | (charCode >>> 18)
67
- outputArray[writeOffset++] = 0x80 | ((charCode >>> 12) & 63)
68
- outputArray[writeOffset++] = 0x80 | ((charCode >>> 6) & 63)
69
- outputArray[writeOffset++] = 0x80 | (charCode & 63)
70
-
71
- readOffset++
72
- }
52
+ //////////////////////////////////////////////////////////////////////////////
53
+ // UTF-8 Decoding
54
+ //////////////////////////////////////////////////////////////////////////////
55
+ export function decodeUtf8(utf8Bytes: Uint8Array) {
56
+ const maxChunkLength = 2 ** 24
57
+
58
+ const chunkedUtf8Decoder = new ChunkedUtf8Decoder()
59
+
60
+ let resultString = ''
61
+
62
+ for (let offset = 0; offset < utf8Bytes.length; offset += maxChunkLength) {
63
+ const utf8Chunk = utf8Bytes.subarray(offset, offset + maxChunkLength)
64
+ const stringChunk = chunkedUtf8Decoder.writeChunk(utf8Chunk)
65
+
66
+ resultString += stringChunk
73
67
  }
74
68
 
75
- return { read: str.length, written: writeOffset }
69
+ return resultString
76
70
  }
77
71
 
78
- function decodeUtf8_JS(utf8Bytes: Uint8Array): string {
79
- let decodedString = ''
80
-
81
- let readOffset = 0
82
-
83
- while (readOffset < utf8Bytes.length) {
84
- const leadByte = utf8Bytes[readOffset++]
85
-
86
- let outputCodePoint: number
87
-
88
- if (leadByte >>> 7 === 0) {
89
- outputCodePoint = leadByte
90
- } else if (leadByte >>> 5 === 6) {
91
- outputCodePoint =
92
- (leadByte & 31) << 6 |
93
- (utf8Bytes[readOffset++] & 63)
94
- } else if (leadByte >>> 4 === 14) {
95
- outputCodePoint =
96
- (leadByte & 15) << 12 |
97
- (utf8Bytes[readOffset++] & 63) << 6 |
98
- (utf8Bytes[readOffset++] & 63)
99
- } else if (leadByte >>> 3 === 30) {
100
- outputCodePoint =
101
- (leadByte & 7) << 18 |
102
- (utf8Bytes[readOffset++] & 63) << 12 |
103
- (utf8Bytes[readOffset++] & 63) << 6 |
104
- (utf8Bytes[readOffset++] & 63)
105
- } else {
106
- throw new Error(`Invalid UTF-8 stream: An invalid lead byte value encountered at position ${readOffset}`)
107
- }
72
+ export class ChunkedUtf8Decoder {
73
+ private readonly textDecoder = new TextDecoder('utf-8')
108
74
 
109
- decodedString += String.fromCodePoint(outputCodePoint)
110
- }
75
+ writeChunk(chunk: Uint8Array) {
76
+ const decodedChunk = this.textDecoder.decode(chunk, { stream: true })
111
77
 
112
- if (readOffset > utf8Bytes.length) {
113
- throw new Error(`UTF-8 decoding failed. Byte sequence is truncated.`)
78
+ return decodedChunk
114
79
  }
115
-
116
- return decodedString
117
80
  }
@@ -1,7 +1,8 @@
1
1
  import { sumArray, logToStderr } from '../utilities/Utilities.js'
2
2
  import { getShortLanguageCode } from '../utilities/Locale.js'
3
3
  import { ParagraphBreakType, WhitespaceProcessing } from '../api/Common.js'
4
- import { includesAnyOf, splitAndPreserveSeparators } from '../utilities/StringUtilities.js'
4
+ import { splitAndPreserveSeparators } from '../utilities/StringUtilities.js'
5
+ import { anyOf, buildRegExp, unicodeProperty, oneOrMore, possibly, codepoint, inputStart, inputEnd, whitespace } from 'regexp-composer'
5
6
 
6
7
  import * as TextSegmentation from '@echogarden/text-segmentation'
7
8
  import { splitChineseTextToWords_Jieba } from './ChineseSegmentation.js'
@@ -9,50 +10,81 @@ import { splitJapaneseTextToWords_Kuromoji } from './JapaneseSegmentation.js'
9
10
 
10
11
  const log = logToStderr
11
12
 
12
- export const wordCharacterRegExp = /[\p{Letter}\p{Number}]/u
13
+ const includesWordCharacterPattern = anyOf(unicodeProperty('Letter'), unicodeProperty('Number'))
14
+ const includesWordCharacterRegExp = buildRegExp(includesWordCharacterPattern)
13
15
 
14
16
  // See: https://mathiasbynens.be/notes/es-unicode-property-escapes
15
- export const emojiSequenceRegExp = /\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F/u
17
+ //export const emojiSequenceRegExp = /\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F/u
18
+ const includesEmojiSequencePattern = anyOf(
19
+ [unicodeProperty('Emoji_Modifier_Base'), possibly(unicodeProperty('Emoji_Modifier'))],
20
+ unicodeProperty('Emoji_Presentation'),
21
+ [unicodeProperty('Emoji'), codepoint('FE0F')]
22
+ )
23
+ const includesEmojiSequenceRegExp = buildRegExp(includesEmojiSequencePattern)
16
24
 
17
- export const includesPunctuationRegExp = /[\p{Punctuation}]/u
18
- export const isAllPunctuationRegExp = /^[\p{Punctuation}]+$/u
25
+ const symbolWordsList = [
26
+ '$', '€', '¢', '£', '¥', '©', '®', '™', '%', '&', '#', '~', '@', '+', '±', '÷',
27
+ '/', '\\', '^', '*', '×', '=', '≈', '¼', '½', '¾', '→', '≤', '≥', '∞'
28
+ ]
19
29
 
20
- export const phraseSeparators = [',', '、', ',', '،', ';', ';', ':', ':', '—']
21
- export const symbolWords = ['$', '€', '¢', '£', '¥', '©', '®', '™', '%', '&', '#', '~', '@', '+', '±', '÷', '/', '\\', '^', '*', '×', '=', '≈', '¼', '½', '¾', '→', '≤', '≥']
30
+ const includesSymbolWordPattern = anyOf(...symbolWordsList)
31
+ const includesSymbolWordRegExp = buildRegExp(includesSymbolWordPattern)
32
+
33
+ const isAllSymbolWordsPattern = [inputStart, oneOrMore(includesSymbolWordPattern), inputEnd]
34
+ const isAllSymbolWordsRegExp = buildRegExp(isAllSymbolWordsPattern)
35
+
36
+ const includesWordCharacterOrEmojiPattern = anyOf(includesWordCharacterPattern, includesEmojiSequencePattern)
37
+ const includesWordCharacterOrEmojiRegExp = buildRegExp(includesWordCharacterOrEmojiPattern)
38
+
39
+ const includesWordCharacterOrEmojiOrIsAllSymbolWordPattern = anyOf(includesWordCharacterPattern, includesEmojiSequencePattern, isAllSymbolWordsPattern)
40
+ const includesWordCharacterOrEmojiOrIsAllSymbolWordRegExp = buildRegExp(includesWordCharacterOrEmojiOrIsAllSymbolWordPattern)
41
+
42
+ const includesPunctuationPattern = unicodeProperty('Punctuation')
43
+ const includesPunctuationRegExp = buildRegExp(includesPunctuationPattern)
44
+
45
+ const isAllPunctuationPattern = [inputStart, oneOrMore(includesPunctuationPattern), inputEnd]
46
+ const isAllPunctuationRegExp = buildRegExp(isAllPunctuationPattern)
47
+
48
+ const phraseSeparatorsList = [',', '、', ',', '،', ';', ';', ':', ':', '—']
49
+ const includesPhraseSeparatorsPattern = anyOf(...phraseSeparatorsList)
50
+ const includesPhraseSeparatorsRegExp = buildRegExp(includesPhraseSeparatorsPattern)
51
+
52
+ const isAllWhitespacePattern = [inputStart, oneOrMore(whitespace), inputEnd]
53
+ const isAllWhitespaceRegExp = buildRegExp(isAllWhitespacePattern)
22
54
 
23
55
  ///////////////////////////////////////////////////////////////////////////////////////////////
24
56
  // Predicates
25
57
  ///////////////////////////////////////////////////////////////////////////////////////////////
26
58
  export function isWordOrEmojiOrSymbolWord(str: string) {
27
- return isWordOrEmoji(str) || symbolWords.includes(str)
59
+ return includesWordCharacterOrEmojiOrIsAllSymbolWordRegExp.test(str.trim())
28
60
  }
29
61
 
30
62
  export function isWordOrEmoji(str: string) {
31
- return isWord(str) || includesEmoji(str)
63
+ return includesWordCharacterOrEmojiRegExp.test(str.trim())
32
64
  }
33
65
 
34
66
  export function isSymbolWord(str: string) {
35
- return symbolWords.includes(str?.trim())
67
+ return isAllSymbolWordsRegExp.test(str.trim())
36
68
  }
37
69
 
38
- export function isWord(str: string) {
39
- return wordCharacterRegExp.test(str?.trim())
70
+ export function includesWordCharacter(str: string) {
71
+ return includesWordCharacterRegExp.test(str.trim())
40
72
  }
41
73
 
42
74
  export function includesPunctuation(str: string) {
43
- return includesPunctuationRegExp.test(str?.trim())
75
+ return includesPunctuationRegExp.test(str.trim())
44
76
  }
45
77
 
46
78
  export function isAllPunctuation(str: string) {
47
- return isAllPunctuationRegExp.test(str?.trim())
79
+ return isAllPunctuationRegExp.test(str.trim())
48
80
  }
49
81
 
50
82
  export function includesEmoji(str: string) {
51
- return emojiSequenceRegExp.test(str?.trim())
83
+ return includesEmojiSequenceRegExp.test(str.trim())
52
84
  }
53
85
 
54
86
  export function isAllWhitespace(str: string) {
55
- return str && /^\s+$/.test(str)
87
+ return isAllWhitespaceRegExp.test(str)
56
88
  }
57
89
 
58
90
  ///////////////////////////////////////////////////////////////////////////////////////////////
@@ -280,11 +312,13 @@ export class Word {
280
312
  this.isSentenceFinalizer = isSentenceFinalizer
281
313
  }
282
314
 
283
- get containsOnlyPunctuation() { return !wordCharacterRegExp.test(this.text) && !this.isSymbolWord }
315
+ get containsOnlyPunctuation() { return !isWordOrEmojiOrSymbolWord(this.text) }
284
316
 
285
- get isSymbolWord() { return symbolWords.includes(this.text) }
317
+ get isSymbolWord() { return isSymbolWord(this.text) }
286
318
 
287
- get isPhraseSeperator() { return this.containsOnlyPunctuation && includesAnyOf(this.text, phraseSeparators) }
319
+ get isPhraseSeperator() {
320
+ return this.containsOnlyPunctuation && includesPhraseSeparatorsRegExp.test(this.text)
321
+ }
288
322
 
289
323
  get length() { return this.text.length }
290
324
  }
@@ -1,6 +1,6 @@
1
1
  import { getShortLanguageCode } from '../utilities/Locale.js'
2
2
  import { substituteCharactersUsingLookup } from '../utilities/StringUtilities.js'
3
- import { anyOf, buildRegExp, charRange, inputEnd, inputStart, repeated, zeroOrMore } from 'regexp-composer'
3
+ import { anyOf, buildRegExp, charRange, inputEnd, inputStart, oneOrMore, repeated, unicodeProperty, zeroOrMore } from 'regexp-composer'
4
4
 
5
5
  export function getNormalizedFragmentsForSpeech(
6
6
  words: string[],
@@ -25,14 +25,24 @@ export function getNormalizedFragmentsForSpeech(
25
25
  const nextNonWhitespaceWord = nextNonWhitespaceWords[0]
26
26
 
27
27
  const originalWordIndex = nonWhitespaceWordOriginalIndex[wordIndex]
28
+
28
29
  const isFollowedByWhitespace = words[originalWordIndex + 1]?.trim().length === 0
30
+ const isSpecialCharacterBeforeYear = ['(', ',', '©'].includes(words[originalWordIndex])
29
31
 
30
- if (wordsPrecedingAYear.includes(lowerCaseWord) &&
31
- isFollowedByWhitespace &&
32
- fourDigitYearPatternRegExp.test(nextNonWhitespaceWord)) {
32
+ //const isWordPrecedingAYear = wordsPrecedingAYear.includes(lowerCaseWord)
33
+ const isWordPrecedingAYear =
34
+ isAllLettersOrApostropheRegExp.test(lowerCaseWord) || isSpecialCharacterBeforeYear
35
+
36
+ const followedByFourDigitYearPattern = fourDigitYearRegExp.test(nextNonWhitespaceWord)
33
37
 
34
- // Normalize a four digit year pattern, e.g. 'in 1995'.
38
+ const isWordPrecedingADecade = wordsPrecedingADecade.includes(lowerCaseWord)
39
+ const followedByFourDigitDecadePattern = fourDigitDecadeRegExp.test(nextNonWhitespaceWord)
35
40
 
41
+ if (isWordPrecedingAYear &&
42
+ (isFollowedByWhitespace || isSpecialCharacterBeforeYear) &&
43
+ followedByFourDigitYearPattern) {
44
+
45
+ // Normalize a four digit year pattern, e.g. 'in 1995'.
36
46
  const normalizedString = normalizeFourDigitYearString(nextNonWhitespaceWord)
37
47
 
38
48
  normalizedFragments.push(word)
@@ -43,11 +53,11 @@ export function getNormalizedFragmentsForSpeech(
43
53
 
44
54
  wordIndex += 1
45
55
  } else if (
46
- wordsPrecedingADecade.includes(lowerCaseWord) &&
56
+ isWordPrecedingADecade &&
47
57
  isFollowedByWhitespace &&
48
- fourDigitDecadePatternRegExp.test(nextNonWhitespaceWord)) {
58
+ followedByFourDigitDecadePattern) {
49
59
 
50
- // Normalize a four digit decade pattern, e.g. 'the 1980s'.
60
+ // Normalize a four digit decade pattern, e.g. 'the 1980s'.
51
61
 
52
62
  const normalizedString = normalizeFourDigitDecadeString(nextNonWhitespaceWord)
53
63
 
@@ -58,7 +68,7 @@ export function getNormalizedFragmentsForSpeech(
58
68
  referenceFragments.push(nextNonWhitespaceWord)
59
69
 
60
70
  wordIndex += 1
61
- } else if (fourDigitYearRangePatternRegExp.test(words.slice(originalWordIndex, originalWordIndex + 3).join(''))) {
71
+ } else if (fourDigitYearRangeRegExp.test(words.slice(originalWordIndex, originalWordIndex + 3).join(''))) {
62
72
  // Normalize a year range pattern, e.g. '1835-1896', ensure there are no spaces between words
63
73
  normalizedFragments.push(normalizeFourDigitYearString(nonWhitespaceWords[wordIndex]))
64
74
  referenceFragments.push(nonWhitespaceWords[wordIndex])
@@ -92,7 +102,7 @@ export function getNormalizedFragmentsForSpeech(
92
102
  const referenceString = word
93
103
  referenceFragments.push(referenceString)
94
104
  }
95
- } else if (followingCurrencyPatternRegExp.test(lowerCaseWord)) {
105
+ } else if (followingCurrencyRegExp.test(lowerCaseWord)) {
96
106
  const currencyWord = currencySymbolsAsWords[currencySymbols.indexOf(lowerCaseWord[lowerCaseWord.length - 1])]
97
107
 
98
108
  const normalizedString = `${word.substring(0, word.length - 1)} ${currencyWord}`
@@ -234,14 +244,14 @@ const wordsFollowingACurrency = [
234
244
 
235
245
  const arabicNumeralPattern = charRange('0', '9')
236
246
 
237
- const numberPattern = [
247
+ const isNumberPattern = [
238
248
  inputStart,
239
249
  arabicNumeralPattern,
240
250
  zeroOrMore(anyOf(arabicNumeralPattern, ',', '.')),
241
251
  inputEnd
242
252
  ]
243
253
 
244
- const numberPatternRegExp = buildRegExp(numberPattern)
254
+ const isNumberRegExp = buildRegExp(isNumberPattern)
245
255
 
246
256
  const precedingCurrencyPattern = [
247
257
  inputStart,
@@ -261,13 +271,27 @@ const followingCurrencyPattern = [
261
271
  inputEnd
262
272
  ]
263
273
 
264
- const followingCurrencyPatternRegExp = buildRegExp(followingCurrencyPattern)
274
+ const followingCurrencyRegExp = buildRegExp(followingCurrencyPattern)
265
275
 
266
276
  const fourDigitYearPattern = [inputStart, repeated(4, arabicNumeralPattern), inputEnd]
267
- const fourDigitYearPatternRegExp = buildRegExp(fourDigitYearPattern)
277
+ const fourDigitYearRegExp = buildRegExp(fourDigitYearPattern)
268
278
 
269
279
  const fourDigitDecadePattern = [inputStart, repeated(3, arabicNumeralPattern), '0s', inputEnd]
270
- const fourDigitDecadePatternRegExp = buildRegExp(fourDigitDecadePattern)
280
+ const fourDigitDecadeRegExp = buildRegExp(fourDigitDecadePattern)
271
281
 
272
282
  const fourDigitYearRangePattern = [inputStart, repeated(4, arabicNumeralPattern), anyOf('-', '–'), repeated(4, arabicNumeralPattern), inputEnd]
273
- const fourDigitYearRangePatternRegExp = buildRegExp(fourDigitYearRangePattern)
283
+ const fourDigitYearRangeRegExp = buildRegExp(fourDigitYearRangePattern)
284
+
285
+ const isAllLettersPattern = [inputStart, oneOrMore(unicodeProperty('Letter')), inputEnd]
286
+ const isAllLettersRegExp = buildRegExp(isAllLettersPattern)
287
+
288
+ const isAllLettersOrApostrophePattern = [
289
+ inputStart,
290
+
291
+ unicodeProperty('Letter'),
292
+ zeroOrMore(anyOf(unicodeProperty('Letter'), `'`)),
293
+
294
+ inputEnd,
295
+ ]
296
+
297
+ const isAllLettersOrApostropheRegExp = buildRegExp(isAllLettersOrApostrophePattern)
@@ -1,6 +1,6 @@
1
- import { Item, LanguageCode, StartStreamTranscriptionCommandInput } from '@aws-sdk/client-transcribe-streaming'
2
- import { wordCharacterRegExp } from '../nlp/Segmentation.js'
3
1
  import * as FFMpegTranscoder from '../codecs/FFMpegTranscoder.js'
2
+ import { Item, LanguageCode, StartStreamTranscriptionCommandInput } from '@aws-sdk/client-transcribe-streaming'
3
+ import { isWordOrEmojiOrSymbolWord } from '../nlp/Segmentation.js'
4
4
  import { Logger } from '../utilities/Logger.js'
5
5
  import { Timeline } from '../utilities/Timeline.js'
6
6
  import { RawAudio } from '../audio/AudioUtilities.js'
@@ -103,7 +103,7 @@ export async function recgonize(rawAudio: RawAudio, languageCode: string, region
103
103
  for (const event of events) {
104
104
  const text = event.Content!
105
105
 
106
- if (!wordCharacterRegExp.test(text)) {
106
+ if (!isWordOrEmojiOrSymbolWord(text)) {
107
107
  continue
108
108
  }
109
109
 
@@ -6,7 +6,7 @@ import { Logger } from '../utilities/Logger.js'
6
6
  import { Timeline } from '../utilities/Timeline.js'
7
7
  import { RawAudio, getRawAudioDuration } from '../audio/AudioUtilities.js'
8
8
  import { concatUint8Arrays } from '../utilities/Utilities.js'
9
- import { escapeHtml } from '../encodings/HtmlEscape.js'
9
+ import { escapeHtml } from 'standard-html-escaper'
10
10
  import { SynthesisCallbacks } from '../api/Synthesis.js'
11
11
 
12
12
  export async function synthesize(
@@ -4,15 +4,15 @@ import { Logger } from '../utilities/Logger.js'
4
4
  import { RawAudio, getEmptyRawAudio } from '../audio/AudioUtilities.js'
5
5
  import { getNormalizedFragmentsForSpeech, simplifyPunctuationCharacters } from '../nlp/TextNormalizer.js'
6
6
  import { ipaPhoneToKirshenbaum } from '../nlp/PhoneConversion.js'
7
- import { isAllPunctuation, isWord, isWordOrEmoji, splitToWords, wordCharacterRegExp } from '../nlp/Segmentation.js'
7
+ import { isAllPunctuation, isWordOrEmoji, splitToWords, isWordOrEmojiOrSymbolWord } from '../nlp/Segmentation.js'
8
8
  import { Lexicon, tryGetFirstLexiconSubstitution } from '../nlp/Lexicon.js'
9
9
  import { phonemizeSentence } from '../nlp/EspeakPhonemizer.js'
10
10
  import { Timeline, TimelineEntry } from '../utilities/Timeline.js'
11
11
  import { extendDeep } from '../utilities/ObjectUtilities.js'
12
- import { escapeHtml } from '../encodings/HtmlEscape.js'
12
+ import { escapeHtml } from 'standard-html-escaper'
13
13
  import * as TextSegmentation from '@echogarden/text-segmentation'
14
14
 
15
- import { getGlobalOption, OperationCallbacks, SynthesisCallbacks } from '../api/API.js'
15
+ import { OperationCallbacks, SynthesisCallbacks } from '../api/API.js'
16
16
  import { loadPackage } from '../utilities/PackageManager.js'
17
17
 
18
18
  import { wrapEmscriptenModuleHeap } from 'wasm-heap-manager'
@@ -73,11 +73,14 @@ export async function preprocessAndSynthesize(text: string, language: string, es
73
73
  for (let i = 0; i < mergedWords.length; i++) {
74
74
  const mergedWord = mergedWords[i]
75
75
 
76
- // Convert isolated groups of vertical bars and em dashes to a comma
77
76
  if (/^[\|│—─–]+$/.test(mergedWord)) {
77
+ // Convert isolated groups of vertical bars or em dashes to a comma
78
78
  mergedWords[i] = ','
79
- } else if (isAllPunctuation(mergedWord)) { // Collapse repeated punctuation
80
- mergedWords[i] = mergedWord[0]
79
+ } else if (isAllPunctuation(mergedWord)) {
80
+ // Collapse repeated punctuation to up to 3 repetitions,
81
+ // Since otherwise eSpeak may go crazy.
82
+
83
+ mergedWords[i] = mergedWord.substring(0, Math.min(3, mergedWord.length))
81
84
  }
82
85
  }
83
86
 
@@ -102,7 +105,12 @@ export async function preprocessAndSynthesize(text: string, language: string, es
102
105
 
103
106
  const { normalizedFragments, referenceFragments } = getNormalizedFragmentsForSpeech(words, nonWhitespaceWords, nonWhitespaceWordsOriginalIndex, language)
104
107
 
105
- const simplifiedFragments = normalizedFragments.map(word => simplifyPunctuationCharacters(word).toLocaleLowerCase())
108
+ const simplifiedFragments = normalizedFragments.map(word => {
109
+ return simplifyPunctuationCharacters(word)
110
+ .toLocaleLowerCase()
111
+ .replaceAll('(', ',')
112
+ .replaceAll(')', ',')
113
+ })
106
114
 
107
115
  if ([`'`].includes(simplifiedFragments[0])) {
108
116
  normalizedFragments[0] = `()`
@@ -150,8 +158,8 @@ export async function preprocessAndSynthesize(text: string, language: string, es
150
158
  {
151
159
  const fragmentWordSequence = new TextSegmentation.WordSequence()
152
160
 
153
- for (let fragment of fragments) {
154
- fragmentWordSequence.addWord(fragment, 0, !wordCharacterRegExp.test(fragment))
161
+ for (let fragment of simplifiedFragments) {
162
+ fragmentWordSequence.addWord(fragment, 0, !isWordOrEmojiOrSymbolWord(fragment))
155
163
  }
156
164
 
157
165
  const wordEntries = referenceTimeline.flatMap(phraseEntry => phraseEntry.timeline!)
@@ -72,12 +72,6 @@ export class KokoroTTS {
72
72
  const voicePrimaryLanguage = voice.languages[0]
73
73
  const voicePrimaryLanguageShort = getShortLanguageCode(voicePrimaryLanguage)
74
74
 
75
- sentenceText = //simplifyPunctuationCharacters(sentence.trim())
76
- sentenceText
77
- .replaceAll('(', ', ')
78
- .replaceAll(')', ', ')
79
- .replaceAll('—', ', ')
80
-
81
75
  const simplifiedSentenceText = simplifyPunctuationCharacters(sentenceText.trim())
82
76
 
83
77
  const voiceLanguage = voiceEntry.languages[0]
@@ -8,7 +8,7 @@ import { concatUint8Arrays, getRandomHexString, logToStderr, sha256AsHex } from
8
8
  import { RawAudio, getEmptyRawAudio, getRawAudioDuration } from '../audio/AudioUtilities.js'
9
9
  import { Timer } from '../utilities/Timer.js'
10
10
  import { decodeUtf8, encodeUtf8 } from '../encodings/Utf8.js'
11
- import { escapeHtml } from '../encodings/HtmlEscape.js'
11
+ import { escapeHtml } from 'standard-html-escaper'
12
12
  import { requestHttp } from 'easier-http-request'
13
13
  import { VoiceListRequestCallbacks, SynthesisCallbacks } from '../api/API.js'
14
14
 
@@ -72,12 +72,6 @@ export class VitsTTS {
72
72
 
73
73
  lengthScale *= baseLengthScale
74
74
 
75
- sentence = //simplifyPunctuationCharacters(sentence.trim())
76
- sentence
77
- .replaceAll('(', ', ')
78
- .replaceAll(')', ', ')
79
- .replaceAll('—', ', ')
80
-
81
75
  const Espeak = await import('../synthesis/EspeakTTS.js')
82
76
 
83
77
  logger.end()