echogarden 2.6.0 → 2.8.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.
- package/data/lexicons/heteronyms.en.json +6 -2
- package/data/lexicons/words.en.json +379 -19
- package/dist/alignment/SemanticTextAlignment.js +6 -7
- package/dist/alignment/SemanticTextAlignment.js.map +1 -1
- package/dist/api/Alignment.js +2 -2
- package/dist/api/Alignment.js.map +1 -1
- package/dist/api/Recognition.js +2 -2
- package/dist/api/Recognition.js.map +1 -1
- package/dist/api/SourceSeparation.js +1 -1
- package/dist/api/SpeechTranslation.js +2 -2
- package/dist/api/SpeechTranslation.js.map +1 -1
- package/dist/api/Synthesis.js +7 -7
- package/dist/api/Synthesis.js.map +1 -1
- package/dist/api/TimelineTranslationAlignment.js +2 -2
- package/dist/api/TimelineTranslationAlignment.js.map +1 -1
- package/dist/api/TranslationAlignment.js +2 -2
- package/dist/api/TranslationAlignment.js.map +1 -1
- package/dist/audio/AudioBufferConversion.d.ts +5 -6
- package/dist/audio/AudioBufferConversion.js +16 -248
- package/dist/audio/AudioBufferConversion.js.map +1 -1
- package/dist/audio/AudioPlayer.js +2 -2
- package/dist/audio/AudioPlayer.js.map +1 -1
- package/dist/audio/AudioRecorder.js +1 -1
- package/dist/audio/AudioRecorder.js.map +1 -1
- package/dist/audio/AudioUtilities.d.ts +2 -8
- package/dist/audio/AudioUtilities.js +4 -3
- package/dist/audio/AudioUtilities.js.map +1 -1
- package/dist/cli/CLI.js +4 -4
- package/dist/cli/CLI.js.map +1 -1
- package/dist/codecs/FFMpegTranscoder.d.ts +1 -4
- package/dist/codecs/TIMITCodec.js +1 -2
- package/dist/codecs/TIMITCodec.js.map +1 -1
- package/dist/denoising/RNNoise.js +5 -5
- package/dist/denoising/RNNoise.js.map +1 -1
- package/dist/dsp/FFT.js +13 -19
- package/dist/dsp/FFT.js.map +1 -1
- package/dist/dsp/Rubberband.js +5 -5
- package/dist/dsp/Rubberband.js.map +1 -1
- package/dist/dsp/Sonic.js +6 -6
- package/dist/dsp/Sonic.js.map +1 -1
- package/dist/dsp/SpeexResampler.js +11 -11
- package/dist/dsp/SpeexResampler.js.map +1 -1
- package/dist/encodings/Ascii.js +1 -1
- package/dist/encodings/Ascii.js.map +1 -1
- package/dist/encodings/Utf16.js +1 -1
- package/dist/encodings/Utf16.js.map +1 -1
- package/dist/encodings/Utf8.js +1 -1
- package/dist/encodings/Utf8.js.map +1 -1
- package/dist/nlp/EspeakPhonemizer.js +1 -1
- package/dist/nlp/EspeakPhonemizer.js.map +1 -1
- package/dist/nlp/Segmentation.d.ts +13 -10
- package/dist/nlp/Segmentation.js +129 -106
- package/dist/nlp/Segmentation.js.map +1 -1
- package/dist/nlp/TextNormalizer.js +74 -42
- package/dist/nlp/TextNormalizer.js.map +1 -1
- package/dist/recognition/AmazonTranscribeSTT.js +2 -2
- package/dist/recognition/AmazonTranscribeSTT.js.map +1 -1
- package/dist/recognition/SileroSTT.js +2 -2
- package/dist/recognition/SileroSTT.js.map +1 -1
- package/dist/recognition/WhisperSTT.js +4 -16
- package/dist/recognition/WhisperSTT.js.map +1 -1
- package/dist/subtitles/Subtitles.js +52 -7
- package/dist/subtitles/Subtitles.js.map +1 -1
- package/dist/synthesis/AwsPollyTTS.d.ts +1 -4
- package/dist/synthesis/CoquiServerTTS.d.ts +1 -4
- package/dist/synthesis/DeepgramTTS.d.ts +1 -4
- package/dist/synthesis/ElevenLabsTTS.d.ts +1 -4
- package/dist/synthesis/ElevenLabsTTS.js +11 -10
- package/dist/synthesis/ElevenLabsTTS.js.map +1 -1
- package/dist/synthesis/EspeakTTS.js +8 -8
- package/dist/synthesis/EspeakTTS.js.map +1 -1
- package/dist/synthesis/FliteTTS.d.ts +1 -4
- package/dist/synthesis/GnuSpeechTTS.d.ts +1 -7
- package/dist/synthesis/GoogleCloudTTS.d.ts +1 -4
- package/dist/synthesis/KokoroTTS.js +21 -10
- package/dist/synthesis/KokoroTTS.js.map +1 -1
- package/dist/synthesis/SapiTTS.js +1 -2
- package/dist/synthesis/SapiTTS.js.map +1 -1
- package/dist/synthesis/SvoxPicoTTS.js +25 -25
- package/dist/synthesis/SvoxPicoTTS.js.map +1 -1
- package/dist/text-translation/DeepLTextTranslation.js +4 -3
- package/dist/text-translation/DeepLTextTranslation.js.map +1 -1
- package/dist/text-translation/NLLBTextTranslation.js +7 -6
- package/dist/text-translation/NLLBTextTranslation.js.map +1 -1
- package/dist/utilities/StringUtilities.d.ts +1 -1
- package/dist/utilities/StringUtilities.js +5 -5
- package/dist/utilities/StringUtilities.js.map +1 -1
- package/dist/utilities/Timeline.d.ts +2 -3
- package/dist/utilities/Timeline.js +96 -147
- package/dist/utilities/Timeline.js.map +1 -1
- package/dist/utilities/WikipediaReader.js +3 -3
- package/dist/utilities/WikipediaReader.js.map +1 -1
- package/dist/voice-activity-detection/WebRtcVAD.js +4 -4
- package/dist/voice-activity-detection/WebRtcVAD.js.map +1 -1
- package/docs/Tasklist.md +1 -6
- package/package.json +13 -11
- package/src/alignment/SemanticTextAlignment.ts +6 -7
- package/src/api/Alignment.ts +2 -2
- package/src/api/Recognition.ts +2 -2
- package/src/api/SourceSeparation.ts +1 -1
- package/src/api/SpeechTranslation.ts +2 -2
- package/src/api/Synthesis.ts +7 -7
- package/src/api/TimelineTranslationAlignment.ts +2 -2
- package/src/api/TranslationAlignment.ts +2 -2
- package/src/audio/AudioBufferConversion.ts +17 -263
- package/src/audio/AudioPlayer.ts +3 -3
- package/src/audio/AudioRecorder.ts +1 -1
- package/src/audio/AudioUtilities.ts +5 -3
- package/src/cli/CLI.ts +4 -4
- package/src/codecs/TIMITCodec.ts +1 -2
- package/src/denoising/RNNoise.ts +5 -5
- package/src/dsp/FFT.ts +22 -19
- package/src/dsp/Rubberband.ts +5 -5
- package/src/dsp/Sonic.ts +6 -6
- package/src/dsp/SpeexResampler.ts +11 -11
- package/src/encodings/Ascii.ts +1 -1
- package/src/encodings/Utf16.ts +1 -1
- package/src/encodings/Utf8.ts +1 -1
- package/src/nlp/EspeakPhonemizer.ts +1 -1
- package/src/nlp/Segmentation.ts +146 -111
- package/src/nlp/TextNormalizer.ts +94 -52
- package/src/recognition/AmazonTranscribeSTT.ts +2 -2
- package/src/recognition/SileroSTT.ts +2 -2
- package/src/recognition/WhisperSTT.ts +4 -18
- package/src/subtitles/Subtitles.ts +69 -8
- package/src/synthesis/ElevenLabsTTS.ts +12 -11
- package/src/synthesis/EspeakTTS.ts +9 -8
- package/src/synthesis/KokoroTTS.ts +24 -12
- package/src/synthesis/SapiTTS.ts +1 -2
- package/src/synthesis/SvoxPicoTTS.ts +27 -25
- package/src/text-translation/DeepLTextTranslation.ts +4 -3
- package/src/text-translation/NLLBTextTranslation.ts +8 -6
- package/src/typings/Fillers.d.ts +0 -16
- package/src/utilities/StringUtilities.ts +5 -6
- package/src/utilities/Timeline.ts +100 -170
- package/src/utilities/WikipediaReader.ts +3 -3
- package/src/voice-activity-detection/WebRtcVAD.ts +5 -4
- package/dist/codecs/WaveCodec.d.ts +0 -19
- package/dist/codecs/WaveCodec.js +0 -224
- package/dist/codecs/WaveCodec.js.map +0 -1
- package/dist/nlp/CompromiseNLP.d.ts +0 -15
- package/dist/nlp/CompromiseNLP.js +0 -79
- package/dist/nlp/CompromiseNLP.js.map +0 -1
- package/dist/utilities/BinaryArrayConversion.d.ts +0 -14
- package/dist/utilities/BinaryArrayConversion.js +0 -92
- package/dist/utilities/BinaryArrayConversion.js.map +0 -1
- package/dist/utilities/WasmMemoryManager.d.ts +0 -153
- package/dist/utilities/WasmMemoryManager.js +0 -440
- package/dist/utilities/WasmMemoryManager.js.map +0 -1
- package/src/codecs/WaveCodec.ts +0 -281
- package/src/nlp/CompromiseNLP.ts +0 -119
- package/src/utilities/BinaryArrayConversion.ts +0 -117
- package/src/utilities/WasmMemoryManager.ts +0 -566
|
@@ -1,5 +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
4
|
|
|
4
5
|
export function getNormalizedFragmentsForSpeech(
|
|
5
6
|
words: string[],
|
|
@@ -13,36 +14,6 @@ export function getNormalizedFragmentsForSpeech(
|
|
|
13
14
|
return { normalizedFragments: [...nonWhitespaceWords], referenceFragments: [...nonWhitespaceWords] }
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
const numberPattern = /^[0-9][0-9\,\.]*$/
|
|
17
|
-
|
|
18
|
-
const fourDigitYearPattern = /^[0-9][0-9][0-9][0-9]$/
|
|
19
|
-
const fourDigitDecadePattern = /^[0-9][0-9][0-9]0s$/
|
|
20
|
-
|
|
21
|
-
const fourDigitYearRangePattern = /^[0-9][0-9][0-9][0-9][\-\–][0-9][0-9][0-9][0-9]$/
|
|
22
|
-
|
|
23
|
-
const wordsPrecedingAYear = [
|
|
24
|
-
'in', 'the', 'a', 'to', 'of', 'since', 'from', 'between', 'by', 'until', 'around', 'before', 'after',
|
|
25
|
-
'his', 'her', 'year', 'years', 'during', 'copyright', '©', 'early', 'mid', 'late',
|
|
26
|
-
'january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december',
|
|
27
|
-
'jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'
|
|
28
|
-
]
|
|
29
|
-
|
|
30
|
-
const wordsPrecedingADecade = [
|
|
31
|
-
'the', 'in', 'early', 'mid', 'late', 'a'
|
|
32
|
-
]
|
|
33
|
-
|
|
34
|
-
const symbolsPrecedingACurrency = [
|
|
35
|
-
'$', '€', '£', '¥'
|
|
36
|
-
]
|
|
37
|
-
|
|
38
|
-
const symbolsPrecedingACurrencyAsWords = [
|
|
39
|
-
'dollars', 'euros', 'pounds', 'yen'
|
|
40
|
-
]
|
|
41
|
-
|
|
42
|
-
const wordsSucceedingACurrency = [
|
|
43
|
-
'million', 'billion', 'trillion'
|
|
44
|
-
]
|
|
45
|
-
|
|
46
17
|
const normalizedFragments: string[] = []
|
|
47
18
|
const referenceFragments: string[] = []
|
|
48
19
|
|
|
@@ -56,10 +27,11 @@ export function getNormalizedFragmentsForSpeech(
|
|
|
56
27
|
const originalWordIndex = nonWhitespaceWordOriginalIndex[wordIndex]
|
|
57
28
|
const isFollowedByWhitespace = words[originalWordIndex + 1]?.trim().length === 0
|
|
58
29
|
|
|
59
|
-
if (
|
|
60
|
-
wordsPrecedingAYear.includes(lowerCaseWord) &&
|
|
30
|
+
if (wordsPrecedingAYear.includes(lowerCaseWord) &&
|
|
61
31
|
isFollowedByWhitespace &&
|
|
62
|
-
|
|
32
|
+
fourDigitYearPatternRegExp.test(nextNonWhitespaceWord)) {
|
|
33
|
+
|
|
34
|
+
// Normalize a four digit year pattern, e.g. 'in 1995'.
|
|
63
35
|
|
|
64
36
|
const normalizedString = normalizeFourDigitYearString(nextNonWhitespaceWord)
|
|
65
37
|
|
|
@@ -70,10 +42,12 @@ export function getNormalizedFragmentsForSpeech(
|
|
|
70
42
|
referenceFragments.push(nextNonWhitespaceWord)
|
|
71
43
|
|
|
72
44
|
wordIndex += 1
|
|
73
|
-
} else if (
|
|
45
|
+
} else if (
|
|
74
46
|
wordsPrecedingADecade.includes(lowerCaseWord) &&
|
|
75
47
|
isFollowedByWhitespace &&
|
|
76
|
-
|
|
48
|
+
fourDigitDecadePatternRegExp.test(nextNonWhitespaceWord)) {
|
|
49
|
+
|
|
50
|
+
// Normalize a four digit decade pattern, e.g. 'the 1980s'.
|
|
77
51
|
|
|
78
52
|
const normalizedString = normalizeFourDigitDecadeString(nextNonWhitespaceWord)
|
|
79
53
|
|
|
@@ -84,9 +58,8 @@ export function getNormalizedFragmentsForSpeech(
|
|
|
84
58
|
referenceFragments.push(nextNonWhitespaceWord)
|
|
85
59
|
|
|
86
60
|
wordIndex += 1
|
|
87
|
-
} else if (
|
|
88
|
-
|
|
89
|
-
|
|
61
|
+
} else if (fourDigitYearRangePatternRegExp.test(words.slice(originalWordIndex, originalWordIndex + 3).join(''))) {
|
|
62
|
+
// Normalize a year range pattern, e.g. '1835-1896', ensure there are no spaces between words
|
|
90
63
|
normalizedFragments.push(normalizeFourDigitYearString(nonWhitespaceWords[wordIndex]))
|
|
91
64
|
referenceFragments.push(nonWhitespaceWords[wordIndex])
|
|
92
65
|
|
|
@@ -97,32 +70,37 @@ export function getNormalizedFragmentsForSpeech(
|
|
|
97
70
|
referenceFragments.push(nonWhitespaceWords[wordIndex + 2])
|
|
98
71
|
|
|
99
72
|
wordIndex += 2
|
|
100
|
-
} else if (
|
|
101
|
-
|
|
102
|
-
!isFollowedByWhitespace &&
|
|
103
|
-
numberPattern.test(nextNonWhitespaceWord)) {
|
|
73
|
+
} else if (precedingCurrencyPatternRegExp.test(lowerCaseWord)) {
|
|
74
|
+
// Normalize a currency pattern with preceding currency symbol, e.g. '€3.53', '$53.1 million',
|
|
104
75
|
|
|
105
|
-
|
|
76
|
+
const currencyWord = currencySymbolsAsWords[currencySymbols.indexOf(lowerCaseWord[0])]
|
|
106
77
|
|
|
107
|
-
if (
|
|
108
|
-
const normalizedString = `${
|
|
78
|
+
if (wordsFollowingACurrency.includes(nextNonWhitespaceWord?.toLowerCase())) {
|
|
79
|
+
const normalizedString = `${word.substring(1)} ${nextNonWhitespaceWord} ${currencyWord}`
|
|
109
80
|
|
|
110
81
|
normalizedFragments.push(normalizedString)
|
|
111
82
|
|
|
112
|
-
const referenceString = `${word}${nextNonWhitespaceWord}
|
|
83
|
+
const referenceString = `${word} ${nextNonWhitespaceWord}`
|
|
113
84
|
referenceFragments.push(referenceString)
|
|
114
85
|
|
|
115
|
-
wordIndex +=
|
|
86
|
+
wordIndex += 1
|
|
116
87
|
} else {
|
|
117
|
-
const normalizedString = `${
|
|
88
|
+
const normalizedString = `${word.substring(1)} ${currencyWord}`
|
|
118
89
|
|
|
119
90
|
normalizedFragments.push(normalizedString)
|
|
120
91
|
|
|
121
|
-
const referenceString =
|
|
92
|
+
const referenceString = word
|
|
122
93
|
referenceFragments.push(referenceString)
|
|
123
|
-
|
|
124
|
-
wordIndex += 1
|
|
125
94
|
}
|
|
95
|
+
} else if (followingCurrencyPatternRegExp.test(lowerCaseWord)) {
|
|
96
|
+
const currencyWord = currencySymbolsAsWords[currencySymbols.indexOf(lowerCaseWord[lowerCaseWord.length - 1])]
|
|
97
|
+
|
|
98
|
+
const normalizedString = `${word.substring(0, word.length - 1)} ${currencyWord}`
|
|
99
|
+
|
|
100
|
+
normalizedFragments.push(normalizedString)
|
|
101
|
+
|
|
102
|
+
const referenceString = word
|
|
103
|
+
referenceFragments.push(referenceString)
|
|
126
104
|
} else {
|
|
127
105
|
normalizedFragments.push(word)
|
|
128
106
|
referenceFragments.push(word)
|
|
@@ -156,7 +134,7 @@ export function normalizeFourDigitDecadeString(decadeString: string) {
|
|
|
156
134
|
let normalizedString: string
|
|
157
135
|
|
|
158
136
|
const isBeforeSecondMillenium = firstTwoDigitsValue < 10
|
|
159
|
-
const isMilleniumDecade =
|
|
137
|
+
const isMilleniumDecade = firstTwoDigitsValue % 10 == 0 && secondTwoDigitsValue == 0
|
|
160
138
|
|
|
161
139
|
if (!isBeforeSecondMillenium && !isMilleniumDecade) {
|
|
162
140
|
if (secondTwoDigitsValue != 0) {
|
|
@@ -229,3 +207,67 @@ export const punctuationSubstitutionLookup: Record<string, string> = {
|
|
|
229
207
|
'!': `!`,
|
|
230
208
|
'¡': `!`,
|
|
231
209
|
}
|
|
210
|
+
|
|
211
|
+
const wordsPrecedingAYear = [
|
|
212
|
+
'in', 'the', 'a', 'to', 'of', 'since', 'from', 'between', 'by', 'until', 'around', 'before', 'after',
|
|
213
|
+
'his', 'her', 'year', 'years', 'during', 'copyright', '©', 'early', 'mid', 'late',
|
|
214
|
+
'january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december',
|
|
215
|
+
'jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec',
|
|
216
|
+
'winter', 'spring', 'summer', 'fall', 'autumn'
|
|
217
|
+
]
|
|
218
|
+
|
|
219
|
+
const wordsPrecedingADecade = [
|
|
220
|
+
'the', 'in', 'early', 'mid', 'late', 'a'
|
|
221
|
+
]
|
|
222
|
+
|
|
223
|
+
const currencySymbols = [
|
|
224
|
+
'$', '¥', '€', '£', '₩', '₭', '₽', '₫', '฿', '¢', '₮', '؋', '₦', '₱', '₴', '₪'
|
|
225
|
+
]
|
|
226
|
+
|
|
227
|
+
const currencySymbolsAsWords = [
|
|
228
|
+
'dollars', 'yen', 'euros', 'pounds', 'won', 'kip', 'rubles', 'dong', 'baht', 'cents', 'tögrög', 'afghanis', 'naira', 'pesos', 'hryvnia', 'shekels'
|
|
229
|
+
]
|
|
230
|
+
|
|
231
|
+
const wordsFollowingACurrency = [
|
|
232
|
+
'million', 'billion', 'trillion'
|
|
233
|
+
]
|
|
234
|
+
|
|
235
|
+
const arabicNumeralPattern = charRange('0', '9')
|
|
236
|
+
|
|
237
|
+
const numberPattern = [
|
|
238
|
+
inputStart,
|
|
239
|
+
arabicNumeralPattern,
|
|
240
|
+
zeroOrMore(anyOf(arabicNumeralPattern, ',', '.')),
|
|
241
|
+
inputEnd
|
|
242
|
+
]
|
|
243
|
+
|
|
244
|
+
const numberPatternRegExp = buildRegExp(numberPattern)
|
|
245
|
+
|
|
246
|
+
const precedingCurrencyPattern = [
|
|
247
|
+
inputStart,
|
|
248
|
+
anyOf(...currencySymbols),
|
|
249
|
+
arabicNumeralPattern,
|
|
250
|
+
zeroOrMore(anyOf(arabicNumeralPattern, ',', '.')),
|
|
251
|
+
inputEnd
|
|
252
|
+
]
|
|
253
|
+
|
|
254
|
+
const precedingCurrencyPatternRegExp = buildRegExp(precedingCurrencyPattern)
|
|
255
|
+
|
|
256
|
+
const followingCurrencyPattern = [
|
|
257
|
+
inputStart,
|
|
258
|
+
arabicNumeralPattern,
|
|
259
|
+
zeroOrMore(anyOf(arabicNumeralPattern, ',', '.')),
|
|
260
|
+
anyOf(...currencySymbols),
|
|
261
|
+
inputEnd
|
|
262
|
+
]
|
|
263
|
+
|
|
264
|
+
const followingCurrencyPatternRegExp = buildRegExp(followingCurrencyPattern)
|
|
265
|
+
|
|
266
|
+
const fourDigitYearPattern = [inputStart, repeated(4, arabicNumeralPattern), inputEnd]
|
|
267
|
+
const fourDigitYearPatternRegExp = buildRegExp(fourDigitYearPattern)
|
|
268
|
+
|
|
269
|
+
const fourDigitDecadePattern = [inputStart, repeated(3, arabicNumeralPattern), '0s', inputEnd]
|
|
270
|
+
const fourDigitDecadePatternRegExp = buildRegExp(fourDigitDecadePattern)
|
|
271
|
+
|
|
272
|
+
const fourDigitYearRangePattern = [inputStart, repeated(4, arabicNumeralPattern), anyOf('-', '–'), repeated(4, arabicNumeralPattern), inputEnd]
|
|
273
|
+
const fourDigitYearRangePatternRegExp = buildRegExp(fourDigitYearRangePattern)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Item, LanguageCode, StartStreamTranscriptionCommandInput } from '@aws-sdk/client-transcribe-streaming'
|
|
2
|
-
import {
|
|
2
|
+
import { wordCharacterRegExp } from '../nlp/Segmentation.js'
|
|
3
3
|
import * as FFMpegTranscoder from '../codecs/FFMpegTranscoder.js'
|
|
4
4
|
import { Logger } from '../utilities/Logger.js'
|
|
5
5
|
import { Timeline } from '../utilities/Timeline.js'
|
|
@@ -102,7 +102,7 @@ export async function recgonize(rawAudio: RawAudio, languageCode: string, region
|
|
|
102
102
|
for (const event of events) {
|
|
103
103
|
const text = event.Content!
|
|
104
104
|
|
|
105
|
-
if (!
|
|
105
|
+
if (!wordCharacterRegExp.test(text)) {
|
|
106
106
|
continue
|
|
107
107
|
}
|
|
108
108
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { indexOfMax } from '../math/VectorMath.js'
|
|
2
|
-
import {
|
|
2
|
+
import { wordCharacterRegExp } from '../nlp/Segmentation.js'
|
|
3
3
|
import { Logger } from '../utilities/Logger.js'
|
|
4
4
|
import { logToStderr } from '../utilities/Utilities.js'
|
|
5
5
|
import { Timeline } from '../utilities/Timeline.js'
|
|
@@ -182,7 +182,7 @@ export class SileroSTT {
|
|
|
182
182
|
for (let i = 0; i < words.length; i++) {
|
|
183
183
|
const text = words[i]
|
|
184
184
|
|
|
185
|
-
if (!
|
|
185
|
+
if (!wordCharacterRegExp.test(text)) {
|
|
186
186
|
continue
|
|
187
187
|
}
|
|
188
188
|
|
|
@@ -18,7 +18,7 @@ import chalk from 'chalk'
|
|
|
18
18
|
import { XorShift32PRNG } from '../utilities/RandomGenerator.js'
|
|
19
19
|
import { detectSpeechLanguageByParts } from '../api/SpeechLanguageDetection.js'
|
|
20
20
|
import { type Tiktoken } from 'tiktoken/lite'
|
|
21
|
-
import {
|
|
21
|
+
import { includesPunctuation, isWhitespace, splitToWords } from '../nlp/Segmentation.js'
|
|
22
22
|
import { medianOf5Filter } from '../math/MedianFilter.js'
|
|
23
23
|
import { getDeflateCompressionMetricsForString } from '../utilities/Compression.js'
|
|
24
24
|
import { dmlProviderAvailable, getOnnxSessionOptions, makeOnnxLikeFloat32Tensor, OnnxExecutionProvider, OnnxLikeFloat32Tensor } from '../utilities/OnnxUtilities.js'
|
|
@@ -528,25 +528,11 @@ export class Whisper {
|
|
|
528
528
|
|
|
529
529
|
const targetLanguage = task === 'transcribe' ? sourceLanguage : 'en'
|
|
530
530
|
|
|
531
|
-
const shouldSplitToSentences = false
|
|
532
531
|
|
|
533
532
|
let simplifiedTranscript = ''
|
|
534
533
|
|
|
535
|
-
|
|
536
|
-
const
|
|
537
|
-
|
|
538
|
-
for (const sentence of sentences) {
|
|
539
|
-
let sentenceWords = await splitToWords(sentence, targetLanguage)
|
|
540
|
-
sentenceWords = sentenceWords.filter(word => isWord(word))
|
|
541
|
-
|
|
542
|
-
simplifiedTranscript += sentenceWords.join(' ')
|
|
543
|
-
simplifiedTranscript += ' '
|
|
544
|
-
}
|
|
545
|
-
} else {
|
|
546
|
-
let words = await splitToWords(transcript, targetLanguage)
|
|
547
|
-
|
|
548
|
-
words = words.map(word => word.trim())
|
|
549
|
-
words = words.filter(word => isWord(word))
|
|
534
|
+
{
|
|
535
|
+
const words = (await splitToWords(transcript, targetLanguage)).nonPunctuationWords
|
|
550
536
|
|
|
551
537
|
simplifiedTranscript = words.join(' ')
|
|
552
538
|
}
|
|
@@ -1253,7 +1239,7 @@ export class Whisper {
|
|
|
1253
1239
|
return false
|
|
1254
1240
|
}
|
|
1255
1241
|
|
|
1256
|
-
return isWhitespace(char) ||
|
|
1242
|
+
return isWhitespace(char) || includesPunctuation(char)
|
|
1257
1243
|
}
|
|
1258
1244
|
|
|
1259
1245
|
function startsWithSeparatorCharacter(text: string) {
|
|
@@ -6,7 +6,8 @@ import { charactersToWriteAhead } from '../audio/AudioPlayer.js'
|
|
|
6
6
|
import { Timeline, TimelineEntry } from '../utilities/Timeline.js'
|
|
7
7
|
import { readFileAsUtf8 } from '../utilities/FileSystem.js'
|
|
8
8
|
import { deepClone } from '../utilities/ObjectUtilities.js'
|
|
9
|
-
import { formatHMS, formatMS
|
|
9
|
+
import { formatHMS, formatMS } from '../utilities/StringUtilities.js'
|
|
10
|
+
import { anyOf, buildRegExp, capture, digit, inputStart, matches, notUnicodeProperty, oneOrMore, zeroOrMore } from 'regexp-composer'
|
|
10
11
|
|
|
11
12
|
export async function subtitlesFileToText(filename: string) {
|
|
12
13
|
return subtitlesToText(await readFileAsUtf8(filename))
|
|
@@ -257,14 +258,12 @@ function getCuesFromTimeline_IsolateSegmentSentence(timeline: Timeline, config:
|
|
|
257
258
|
const lineLengthWithNextWord = nextWordExtendedEndOffset - lineStartOffset
|
|
258
259
|
const wordsRemaining = wordTimeline.length - wordIndex - 1
|
|
259
260
|
|
|
260
|
-
const phraseSeparators = [',', ',', '、', ';', ':', '),', '",', '”,', '.', '".', '”.', '."', '.”', '。']
|
|
261
|
-
|
|
262
261
|
const lineLengthWithNextWordExceedsMaxLineWidth = lineLengthWithNextWord >= maxLineWidth
|
|
263
262
|
const lineLengthExceedsHalfMaxLineWidth = lineLength >= maxLineWidth / 2
|
|
264
263
|
|
|
265
264
|
const wordsRemainingAreEqualOrLessToMinimumWordsInLine = wordsRemaining <= config.minWordsInLine!
|
|
266
265
|
const remainingTextExceedsMaxLineWidth = entryText.length - lineStartOffset > maxLineWidth
|
|
267
|
-
const followingSubstringIsPhraseSeparator =
|
|
266
|
+
const followingSubstringIsPhraseSeparator = phraseSeparatorRegExp.test(entryText.substring(wordEndOffset))
|
|
268
267
|
|
|
269
268
|
const shouldAddNewLine =
|
|
270
269
|
isLastWord ||
|
|
@@ -434,8 +433,7 @@ function getCuesFromTimeline_IsolateLines(timeline: Timeline, config: SubtitlesC
|
|
|
434
433
|
}
|
|
435
434
|
|
|
436
435
|
export function tryParseTimeRangePatternWithHours(line: string) {
|
|
437
|
-
const
|
|
438
|
-
const match = timeRangePatternWithHours.exec(line)
|
|
436
|
+
const match = timeRangeWithHoursRegExp.exec(line)
|
|
439
437
|
|
|
440
438
|
if (!match) {
|
|
441
439
|
return { startTime: -1, endTime: -1, succeeded: false }
|
|
@@ -458,8 +456,7 @@ export function tryParseTimeRangePatternWithHours(line: string) {
|
|
|
458
456
|
}
|
|
459
457
|
|
|
460
458
|
export function tryParseTimeRangePatternWithoutHours(line: string) {
|
|
461
|
-
const
|
|
462
|
-
const match = timeRangePatternWithHours.exec(line)
|
|
459
|
+
const match = timeRangeWithoutHoursRegExp.exec(line)
|
|
463
460
|
|
|
464
461
|
if (!match) {
|
|
465
462
|
return { startTime: -1, endTime: -1, succeeded: false }
|
|
@@ -514,6 +511,70 @@ function cueObjectToText(cue: Cue, cueIndex: number, config: SubtitlesConfig) {
|
|
|
514
511
|
return outText
|
|
515
512
|
}
|
|
516
513
|
|
|
514
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
515
|
+
// Patterns
|
|
516
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
517
|
+
const timeRangeWithHoursRegExp = buildRegExp([
|
|
518
|
+
inputStart,
|
|
519
|
+
capture(oneOrMore(digit)),
|
|
520
|
+
':',
|
|
521
|
+
capture(oneOrMore(digit)),
|
|
522
|
+
':',
|
|
523
|
+
capture(oneOrMore(digit)),
|
|
524
|
+
|
|
525
|
+
anyOf('.', ','),
|
|
526
|
+
capture(oneOrMore(digit)),
|
|
527
|
+
|
|
528
|
+
zeroOrMore(' '),
|
|
529
|
+
'-->',
|
|
530
|
+
zeroOrMore(' '),
|
|
531
|
+
|
|
532
|
+
capture(oneOrMore(digit)),
|
|
533
|
+
':',
|
|
534
|
+
capture(oneOrMore(digit)),
|
|
535
|
+
':',
|
|
536
|
+
capture(oneOrMore(digit)),
|
|
537
|
+
|
|
538
|
+
anyOf('.', ','),
|
|
539
|
+
capture(oneOrMore(digit)),
|
|
540
|
+
])
|
|
541
|
+
|
|
542
|
+
const timeRangeWithoutHoursRegExp = buildRegExp([
|
|
543
|
+
inputStart,
|
|
544
|
+
capture(oneOrMore(digit)),
|
|
545
|
+
':',
|
|
546
|
+
capture(oneOrMore(digit)),
|
|
547
|
+
|
|
548
|
+
anyOf('.', ','),
|
|
549
|
+
capture(oneOrMore(digit)),
|
|
550
|
+
|
|
551
|
+
zeroOrMore(' '),
|
|
552
|
+
'-->',
|
|
553
|
+
zeroOrMore(' '),
|
|
554
|
+
|
|
555
|
+
capture(oneOrMore(digit)),
|
|
556
|
+
':',
|
|
557
|
+
capture(oneOrMore(digit)),
|
|
558
|
+
|
|
559
|
+
anyOf('.', ','),
|
|
560
|
+
capture(oneOrMore(digit)),
|
|
561
|
+
])
|
|
562
|
+
|
|
563
|
+
const phraseSeparatorCharacters = [',', ',', '、', ';', ':', '),', '",', '”,']
|
|
564
|
+
|
|
565
|
+
const phraseSeparatorRegExp = buildRegExp(
|
|
566
|
+
matches([
|
|
567
|
+
inputStart,
|
|
568
|
+
anyOf(...phraseSeparatorCharacters)
|
|
569
|
+
], {
|
|
570
|
+
ifPrecededBy: notUnicodeProperty('Decimal_Number'),
|
|
571
|
+
ifFollowedBy: notUnicodeProperty('Decimal_Number'),
|
|
572
|
+
})
|
|
573
|
+
)
|
|
574
|
+
|
|
575
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
576
|
+
// Types
|
|
577
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
517
578
|
export type Cue = {
|
|
518
579
|
lines: string[]
|
|
519
580
|
startTime: number
|
|
@@ -5,7 +5,7 @@ import { Logger } from '../utilities/Logger.js'
|
|
|
5
5
|
import { logToStderr } from '../utilities/Utilities.js'
|
|
6
6
|
import { extendDeep } from '../utilities/ObjectUtilities.js'
|
|
7
7
|
import { decodeBase64 } from '../encodings/Base64.js'
|
|
8
|
-
import {
|
|
8
|
+
import { splitToWords } from '../nlp/Segmentation.js'
|
|
9
9
|
import { Timeline } from '../utilities/Timeline.js'
|
|
10
10
|
|
|
11
11
|
const log = logToStderr
|
|
@@ -79,24 +79,25 @@ export async function synthesize(text: string, voiceId: string, language: string
|
|
|
79
79
|
logger.start('Create timeline from returned character timings')
|
|
80
80
|
|
|
81
81
|
const referenceText = characters.join('')
|
|
82
|
-
const
|
|
82
|
+
const wordSequence = await splitToWords(referenceText, language)
|
|
83
83
|
|
|
84
84
|
timeline = []
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
for (const wordEntry of wordSequence.entries) {
|
|
87
|
+
const wordText = wordEntry.text
|
|
87
88
|
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
|
|
89
|
+
const wordStartOffset = wordEntry.startOffset
|
|
90
|
+
const wordEndOffset = wordEntry.endOffset
|
|
91
|
+
|
|
92
|
+
const startTime = characterStartTimes[wordStartOffset]
|
|
93
|
+
const endTime = characterEndTimes[wordEndOffset] ?? characterEndTimes[wordEndOffset - 1]
|
|
91
94
|
|
|
92
95
|
timeline.push({
|
|
93
96
|
type: 'word',
|
|
94
|
-
text:
|
|
95
|
-
startTime
|
|
96
|
-
endTime
|
|
97
|
+
text: wordText,
|
|
98
|
+
startTime,
|
|
99
|
+
endTime,
|
|
97
100
|
})
|
|
98
|
-
|
|
99
|
-
offset = wordEndIndex
|
|
100
101
|
}
|
|
101
102
|
}
|
|
102
103
|
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { concatFloat32Arrays, logToStderr, formatObjectToString } from '../utilities/Utilities.js'
|
|
2
2
|
import { int16PcmToFloat32 } from '../audio/AudioBufferConversion.js'
|
|
3
3
|
import { Logger } from '../utilities/Logger.js'
|
|
4
|
-
import { WasmMemoryManager } from '../utilities/WasmMemoryManager.js'
|
|
5
4
|
import { RawAudio, getEmptyRawAudio } from '../audio/AudioUtilities.js'
|
|
6
5
|
import { getNormalizedFragmentsForSpeech, simplifyPunctuationCharacters } from '../nlp/TextNormalizer.js'
|
|
7
6
|
import { ipaPhoneToKirshenbaum } from '../nlp/PhoneConversion.js'
|
|
8
|
-
import { splitToWords,
|
|
7
|
+
import { splitToWords, wordCharacterRegExp } from '../nlp/Segmentation.js'
|
|
9
8
|
import { Lexicon, tryGetFirstLexiconSubstitution } from '../nlp/Lexicon.js'
|
|
10
9
|
import { phonemizeSentence } from '../nlp/EspeakPhonemizer.js'
|
|
11
10
|
import { Timeline, TimelineEntry } from '../utilities/Timeline.js'
|
|
12
11
|
import { extendDeep } from '../utilities/ObjectUtilities.js'
|
|
13
12
|
import { escapeHtml } from '../encodings/HtmlEscape.js'
|
|
14
13
|
|
|
14
|
+
import { wrapEmscriptenModuleHeap } from 'wasm-heap-manager'
|
|
15
|
+
|
|
15
16
|
const log = logToStderr
|
|
16
17
|
|
|
17
18
|
let espeakInstance: any
|
|
@@ -37,7 +38,7 @@ export async function preprocessAndSynthesize(text: string, language: string, es
|
|
|
37
38
|
fragments = []
|
|
38
39
|
preprocessedFragments = []
|
|
39
40
|
|
|
40
|
-
let words = await splitToWords(text, language)
|
|
41
|
+
let words = (await splitToWords(text, language)).wordArray
|
|
41
42
|
|
|
42
43
|
// Merge repeating non-words to a single word to work around eSpeak bug
|
|
43
44
|
{
|
|
@@ -51,7 +52,7 @@ export async function preprocessAndSynthesize(text: string, language: string, es
|
|
|
51
52
|
i > 0 &&
|
|
52
53
|
currentWord === previousWord &&
|
|
53
54
|
!['[', ']'].includes(currentWord) && // Work around eSpeak-NG marker bug with repeating squared brackets
|
|
54
|
-
!
|
|
55
|
+
!wordCharacterRegExp.test(currentWord)) {
|
|
55
56
|
|
|
56
57
|
wordsWithMerges[wordsWithMerges.length - 1] += currentWord
|
|
57
58
|
} else {
|
|
@@ -515,12 +516,12 @@ export async function textToPhonemes(text: string, voice: string, useIPA = true)
|
|
|
515
516
|
const { instance, module } = await getEspeakInstance()
|
|
516
517
|
const textPtr = instance.convert_to_phonemes(text, useIPA)
|
|
517
518
|
|
|
518
|
-
const
|
|
519
|
+
const wasmHeap = wrapEmscriptenModuleHeap(module)
|
|
519
520
|
|
|
520
|
-
const resultRef =
|
|
521
|
-
const result = resultRef.
|
|
521
|
+
const resultRef = wasmHeap.wrapNullTerminatedUtf8String(textPtr.ptr)
|
|
522
|
+
const result = resultRef.value
|
|
522
523
|
|
|
523
|
-
|
|
524
|
+
resultRef.free()
|
|
524
525
|
|
|
525
526
|
return result
|
|
526
527
|
}
|
|
@@ -108,9 +108,6 @@ export class KokoroTTS {
|
|
|
108
108
|
const {
|
|
109
109
|
referenceSynthesizedAudio,
|
|
110
110
|
referenceTimeline,
|
|
111
|
-
fragments,
|
|
112
|
-
phonemizedFragmentsSubstitutions,
|
|
113
|
-
phonemizedSentence
|
|
114
111
|
} = await Espeak.preprocessAndSynthesize(sentenceText, voiceLanguage, espeakOptions, lexicons)
|
|
115
112
|
|
|
116
113
|
logger.end()
|
|
@@ -132,13 +129,28 @@ export class KokoroTTS {
|
|
|
132
129
|
|
|
133
130
|
const allTokenIds: number[] = []
|
|
134
131
|
|
|
135
|
-
for (let
|
|
136
|
-
const
|
|
132
|
+
for (let clauseIndex = 0; clauseIndex < referenceTimeline.length; clauseIndex++) {
|
|
133
|
+
const clauseEntry = referenceTimeline[clauseIndex]
|
|
137
134
|
|
|
138
|
-
|
|
139
|
-
|
|
135
|
+
if (!clauseEntry.timeline) {
|
|
136
|
+
continue
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const wordTimelineForClause = clauseEntry.timeline
|
|
140
|
+
|
|
141
|
+
for (let wordIndex = 0; wordIndex < wordTimelineForClause.length; wordIndex++) {
|
|
142
|
+
const wordEntry = wordTimelineForClause[wordIndex]
|
|
143
|
+
const wordText = wordEntry.text
|
|
144
|
+
|
|
145
|
+
if (!wordEntry.timeline) {
|
|
146
|
+
continue
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const tokenTimelineForWord = wordEntry.timeline
|
|
150
|
+
const phonemeTimelineForWord = tokenTimelineForWord.flatMap(tokenEntry => tokenEntry.timeline ?? [])
|
|
140
151
|
|
|
141
|
-
for (const
|
|
152
|
+
for (const phonemeEntry of phonemeTimelineForWord) {
|
|
153
|
+
const phoneme = phonemeEntry.text
|
|
142
154
|
let processedPhoneme = phoneme
|
|
143
155
|
|
|
144
156
|
if (voicePrimaryLanguageShort === 'en') {
|
|
@@ -179,7 +191,7 @@ export class KokoroTTS {
|
|
|
179
191
|
|
|
180
192
|
if (false) {
|
|
181
193
|
// Workaround a word having only 'I' not being pronounced at some cases
|
|
182
|
-
if (processedPhoneme === 'I' &&
|
|
194
|
+
if (processedPhoneme === 'I' && wordText.length === 1) {
|
|
183
195
|
processedPhoneme = 'aɪ'
|
|
184
196
|
//processedPhoneme = 'ˌI'
|
|
185
197
|
}
|
|
@@ -196,10 +208,10 @@ export class KokoroTTS {
|
|
|
196
208
|
}
|
|
197
209
|
}
|
|
198
210
|
|
|
199
|
-
if (wordIndex <
|
|
211
|
+
if (wordIndex < wordTimelineForClause.length - 1) {
|
|
200
212
|
allTokenIds.push(wordBreakTokenId)
|
|
201
213
|
} else {
|
|
202
|
-
if (
|
|
214
|
+
if (clauseIndex < referenceTimeline.length - 1) {
|
|
203
215
|
allTokenIds.push(phraseBreakTokenId)
|
|
204
216
|
allTokenIds.push(wordBreakTokenId)
|
|
205
217
|
}
|
|
@@ -209,7 +221,7 @@ export class KokoroTTS {
|
|
|
209
221
|
|
|
210
222
|
allTokenIds.push(sentenceEndTokenId)
|
|
211
223
|
|
|
212
|
-
const maxPartLength =
|
|
224
|
+
const maxPartLength = 500
|
|
213
225
|
|
|
214
226
|
const parts: number[][] = []
|
|
215
227
|
|
package/src/synthesis/SapiTTS.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { SynthesisVoice } from '../api/API.js'
|
|
2
|
-
import { decodeToChannels } from '../audio/AudioBufferConversion.js'
|
|
2
|
+
import { decodeToChannels, SampleFormat } from '../audio/AudioBufferConversion.js'
|
|
3
3
|
import { RawAudio } from '../audio/AudioUtilities.js'
|
|
4
|
-
import { SampleFormat } from '../codecs/WaveCodec.js'
|
|
5
4
|
import { getShortLanguageCode, lcidToIsoLanguageCode } from '../utilities/Locale.js'
|
|
6
5
|
import { Logger } from '../utilities/Logger.js'
|
|
7
6
|
import { Timeline, TimelineEntry } from '../utilities/Timeline.js'
|