echogarden 0.12.2 → 1.0.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.
- package/README.md +15 -14
- package/data/schemas/options.json +398 -111
- package/dist/alignment/DTWMfccSequenceAlignment.d.ts +1 -1
- package/dist/alignment/DTWMfccSequenceAlignment.js +8 -8
- package/dist/alignment/DTWSequenceAlignment.d.ts +1 -1
- package/dist/alignment/DTWSequenceAlignment.js +1 -1
- package/dist/alignment/DTWSequenceAlignmentWindowed.d.ts +1 -1
- package/dist/alignment/DTWSequenceAlignmentWindowed.js +2 -2
- package/dist/alignment/LevenshteinSequenceAlignment.d.ts +1 -1
- package/dist/alignment/LevenshteinSequenceAlignment.js +1 -1
- package/dist/alignment/SpeechAlignment.d.ts +11 -6
- package/dist/alignment/SpeechAlignment.js +113 -71
- package/dist/alignment/SpeechAlignment.js.map +1 -1
- package/dist/api/API.d.ts +13 -12
- package/dist/api/API.js +14 -13
- package/dist/api/API.js.map +1 -1
- package/dist/api/APIOptions.d.ts +5 -4
- package/dist/api/Alignment.d.ts +15 -9
- package/dist/api/Alignment.js +92 -74
- package/dist/api/Alignment.js.map +1 -1
- package/dist/api/Common.js +1 -1
- package/dist/api/Denoising.d.ts +6 -6
- package/dist/api/Denoising.js +23 -23
- package/dist/api/Denoising.js.map +1 -1
- package/dist/api/LanguageDetection.d.ts +19 -12
- package/dist/api/LanguageDetection.js +88 -38
- package/dist/api/LanguageDetection.js.map +1 -1
- package/dist/api/Recognition.d.ts +16 -6
- package/dist/api/Recognition.js +129 -55
- package/dist/api/Recognition.js.map +1 -1
- package/dist/api/SourceSeparation.d.ts +17 -0
- package/dist/api/SourceSeparation.js +61 -0
- package/dist/api/SourceSeparation.js.map +1 -0
- package/dist/api/Synthesis.d.ts +18 -18
- package/dist/api/Synthesis.js +191 -164
- package/dist/api/Synthesis.js.map +1 -1
- package/dist/api/Translation.d.ts +19 -8
- package/dist/api/Translation.js +132 -35
- package/dist/api/Translation.js.map +1 -1
- package/dist/api/Vad.d.ts +10 -5
- package/dist/api/Vad.js +76 -38
- package/dist/api/Vad.js.map +1 -1
- package/dist/audio/AudioBufferConversion.d.ts +1 -1
- package/dist/audio/AudioBufferConversion.js +4 -4
- package/dist/audio/AudioPlayer.d.ts +1 -1
- package/dist/audio/AudioPlayer.js +26 -26
- package/dist/audio/AudioPlayer.js.map +1 -1
- package/dist/audio/AudioRecorder.d.ts +1 -1
- package/dist/audio/AudioRecorder.js +5 -5
- package/dist/audio/AudioUtilities.d.ts +13 -9
- package/dist/audio/AudioUtilities.js +86 -24
- package/dist/audio/AudioUtilities.js.map +1 -1
- package/dist/cli/CLI.d.ts +3 -3
- package/dist/cli/CLI.js +271 -162
- package/dist/cli/CLI.js.map +1 -1
- package/dist/cli/CLIConfigFile.js +8 -8
- package/dist/cli/CLILauncher.js +6 -6
- package/dist/cli/CLIOptionsSchema.js +2 -2
- package/dist/cli/CLIParser.js +5 -5
- package/dist/cli/CLIStarter.js +4 -4
- package/dist/codecs/FFMpegTranscoder.d.ts +2 -2
- package/dist/codecs/FFMpegTranscoder.js +37 -37
- package/dist/codecs/FFMpegTranscoder.js.map +1 -1
- package/dist/codecs/TIMITCodec.js +5 -5
- package/dist/codecs/WaveCodec.d.ts +1 -1
- package/dist/codecs/WaveCodec.js +22 -22
- package/dist/denoising/RNNoise.d.ts +1 -1
- package/dist/denoising/RNNoise.js +9 -9
- package/dist/dsp/BiquadFilter.d.ts +3 -2
- package/dist/dsp/BiquadFilter.js +18 -11
- package/dist/dsp/BiquadFilter.js.map +1 -1
- package/dist/dsp/DecayingPeakEstimator.d.ts +16 -0
- package/dist/dsp/DecayingPeakEstimator.js +23 -0
- package/dist/dsp/DecayingPeakEstimator.js.map +1 -0
- package/dist/dsp/FFT.d.ts +8 -4
- package/dist/dsp/FFT.js +76 -30
- package/dist/dsp/FFT.js.map +1 -1
- package/dist/dsp/KWeightingFilter.d.ts +9 -0
- package/dist/dsp/KWeightingFilter.js +40 -0
- package/dist/dsp/KWeightingFilter.js.map +1 -0
- package/dist/dsp/LoudnessEstimator.d.ts +21 -0
- package/dist/dsp/LoudnessEstimator.js +47 -0
- package/dist/dsp/LoudnessEstimator.js.map +1 -0
- package/dist/dsp/MFCC.d.ts +2 -2
- package/dist/dsp/MFCC.js +15 -15
- package/dist/dsp/MelSpectogram.d.ts +1 -1
- package/dist/dsp/MelSpectogram.js +6 -6
- package/dist/dsp/Rubberband.d.ts +11 -11
- package/dist/dsp/Rubberband.js +27 -27
- package/dist/dsp/Sonic.d.ts +1 -1
- package/dist/dsp/Sonic.js +3 -3
- package/dist/dsp/SpeexResampler.d.ts +1 -1
- package/dist/dsp/SpeexResampler.js +2 -2
- package/dist/math/VectorMath.d.ts +12 -8
- package/dist/math/VectorMath.js +35 -32
- package/dist/math/VectorMath.js.map +1 -1
- package/dist/nlp/ChineseSegmentation.js +2 -2
- package/dist/nlp/CompromiseNLP.js +3 -3
- package/dist/nlp/EspeakPhonemizer.js +30 -30
- package/dist/nlp/IPA.js +20 -20
- package/dist/nlp/JapaneseSegmentation.js +6 -6
- package/dist/nlp/Lexicon.d.ts +1 -1
- package/dist/nlp/Lexicon.js +7 -7
- package/dist/nlp/Segmentation.d.ts +3 -0
- package/dist/nlp/Segmentation.js +21 -14
- package/dist/nlp/Segmentation.js.map +1 -1
- package/dist/nlp/TextNormalizer.js +16 -16
- package/dist/recognition/AmazonTranscribeSTT.d.ts +2 -2
- package/dist/recognition/AmazonTranscribeSTT.js +13 -14
- package/dist/recognition/AmazonTranscribeSTT.js.map +1 -1
- package/dist/recognition/AzureCognitiveServicesSTT.js +5 -6
- package/dist/recognition/AzureCognitiveServicesSTT.js.map +1 -1
- package/dist/recognition/GoogleCloudSTT.d.ts +3 -3
- package/dist/recognition/GoogleCloudSTT.js +18 -18
- package/dist/recognition/OpenAICloudSTT.d.ts +19 -0
- package/dist/recognition/OpenAICloudSTT.js +81 -0
- package/dist/recognition/OpenAICloudSTT.js.map +1 -0
- package/dist/recognition/SileroSTT.d.ts +2 -2
- package/dist/recognition/SileroSTT.js +25 -25
- package/dist/recognition/VoskSTT.d.ts +2 -2
- package/dist/recognition/VoskSTT.js +8 -8
- package/dist/recognition/WhisperCppSTT.d.ts +88 -0
- package/dist/recognition/WhisperCppSTT.js +332 -0
- package/dist/recognition/WhisperCppSTT.js.map +1 -0
- package/dist/recognition/WhisperSTT.d.ts +49 -25
- package/dist/recognition/WhisperSTT.js +626 -481
- package/dist/recognition/WhisperSTT.js.map +1 -1
- package/dist/server/Client.d.ts +1 -1
- package/dist/server/Client.js +22 -22
- package/dist/server/Server.js +9 -9
- package/dist/server/Server.js.map +1 -1
- package/dist/server/Worker.d.ts +22 -22
- package/dist/server/Worker.js +36 -36
- package/dist/server/Worker.js.map +1 -1
- package/dist/server/WorkerStarter.js +2 -2
- package/dist/source-separation/MDXNetSourceSeparation.d.ts +11 -0
- package/dist/source-separation/MDXNetSourceSeparation.js +161 -0
- package/dist/source-separation/MDXNetSourceSeparation.js.map +1 -0
- package/dist/speech-language-detection/SileroLanguageDetection.d.ts +1 -1
- package/dist/speech-language-detection/SileroLanguageDetection.js +7 -7
- package/dist/subtitles/Subtitles.d.ts +10 -0
- package/dist/subtitles/Subtitles.js +2 -2
- package/dist/subtitles/Subtitles.js.map +1 -1
- package/dist/synthesis/AwsPollyTTS.d.ts +1 -1
- package/dist/synthesis/AwsPollyTTS.js +12 -12
- package/dist/synthesis/AzureCognitiveServicesTTS.js +7 -7
- package/dist/synthesis/CoquiServerTTS.js +10 -10
- package/dist/synthesis/CoquiServerTTS.js.map +1 -1
- package/dist/synthesis/ElevenlabsTTS.d.ts +23 -0
- package/dist/synthesis/ElevenlabsTTS.js +103 -0
- package/dist/synthesis/ElevenlabsTTS.js.map +1 -0
- package/dist/synthesis/EspeakTTS.d.ts +6 -5
- package/dist/synthesis/EspeakTTS.js +81 -69
- package/dist/synthesis/EspeakTTS.js.map +1 -1
- package/dist/synthesis/FliteTTS.d.ts +3 -3
- package/dist/synthesis/FliteTTS.js +154 -154
- package/dist/synthesis/FliteTTS.js.map +1 -1
- package/dist/synthesis/GoogleCloudTTS.d.ts +3 -3
- package/dist/synthesis/GoogleCloudTTS.js +17 -17
- package/dist/synthesis/GoogleCloudTTS.js.map +1 -1
- package/dist/synthesis/GoogleTranslateTTS.d.ts +1 -1
- package/dist/synthesis/GoogleTranslateTTS.js +103 -103
- package/dist/synthesis/MicrosoftEdgeTTS.d.ts +2 -2
- package/dist/synthesis/MicrosoftEdgeTTS.js +74 -74
- package/dist/synthesis/OpenAICloudTTS.d.ts +13 -0
- package/dist/synthesis/OpenAICloudTTS.js +169 -0
- package/dist/synthesis/OpenAICloudTTS.js.map +1 -0
- package/dist/synthesis/SamTTS.js +3 -3
- package/dist/synthesis/SapiTTS.d.ts +3 -3
- package/dist/synthesis/SapiTTS.js +26 -26
- package/dist/synthesis/StreamlabsPollyTTS.d.ts +2 -2
- package/dist/synthesis/StreamlabsPollyTTS.js +27 -27
- package/dist/synthesis/SvoxPicoTTS.d.ts +2 -2
- package/dist/synthesis/SvoxPicoTTS.js +65 -65
- package/dist/synthesis/SvoxPicoTTS.js.map +1 -1
- package/dist/synthesis/VitsTTS.d.ts +3 -3
- package/dist/synthesis/VitsTTS.js +378 -378
- package/dist/synthesis/VitsTTS.js.map +1 -1
- package/dist/tests/Test.js +2 -2
- package/dist/utilities/Compression.d.ts +5 -0
- package/dist/utilities/Compression.js +29 -13
- package/dist/utilities/Compression.js.map +1 -1
- package/dist/utilities/FileDownloader.d.ts +1 -1
- package/dist/utilities/FileDownloader.js +16 -16
- package/dist/utilities/FileSystem.js +7 -7
- package/dist/utilities/Locale.d.ts +7 -7
- package/dist/utilities/Locale.js +15 -15
- package/dist/utilities/Logger.js +3 -3
- package/dist/utilities/ObjectUtilities.js +19 -19
- package/dist/utilities/OpenPromise.js +2 -2
- package/dist/utilities/OpenPromise.js.map +1 -1
- package/dist/utilities/PackageManager.js +31 -0
- package/dist/utilities/PackageManager.js.map +1 -1
- package/dist/utilities/PathUtilities.js +8 -8
- package/dist/utilities/RandomGenerator.js +2 -2
- package/dist/utilities/SmoothEstimator.d.ts +8 -0
- package/dist/utilities/SmoothEstimator.js +25 -0
- package/dist/utilities/SmoothEstimator.js.map +1 -0
- package/dist/utilities/TarballMaker.js +8 -8
- package/dist/utilities/Timeline.d.ts +3 -2
- package/dist/utilities/Timeline.js +11 -11
- package/dist/utilities/Timeline.js.map +1 -1
- package/dist/utilities/Timer.js +4 -4
- package/dist/utilities/Utilities.d.ts +4 -0
- package/dist/utilities/Utilities.js +38 -15
- package/dist/utilities/Utilities.js.map +1 -1
- package/dist/utilities/WasmMemoryManager.js +7 -7
- package/dist/utilities/WebReader.js +23 -23
- package/dist/utilities/WikipediaReader.js +2 -2
- package/dist/voice-activity-detection/AdaptiveGateVAD.d.ts +28 -0
- package/dist/voice-activity-detection/AdaptiveGateVAD.js +138 -0
- package/dist/voice-activity-detection/AdaptiveGateVAD.js.map +1 -0
- package/dist/voice-activity-detection/SileroVAD.d.ts +1 -1
- package/dist/voice-activity-detection/SileroVAD.js +5 -5
- package/dist/voice-activity-detection/SileroVAD.js.map +1 -1
- package/dist/voice-activity-detection/WebRtcVAD.d.ts +1 -1
- package/dist/voice-activity-detection/WebRtcVAD.js +4 -4
- package/docs/API.md +29 -11
- package/docs/CLI.md +31 -7
- package/docs/Contributing.md +38 -0
- package/docs/Development.md +91 -19
- package/docs/Engines.md +28 -16
- package/docs/Licenses.md +4 -1
- package/docs/Options.md +159 -79
- package/docs/Releases.md +262 -0
- package/docs/Server.md +7 -7
- package/docs/Tasklist.md +96 -76
- package/docs/Technical.md +4 -4
- package/package.json +13 -14
- package/src/alignment/DTWMfccSequenceAlignment.ts +9 -9
- package/src/alignment/DTWSequenceAlignment.ts +2 -2
- package/src/alignment/DTWSequenceAlignmentWindowed.ts +3 -3
- package/src/alignment/LevenshteinSequenceAlignment.ts +2 -2
- package/src/alignment/SpeechAlignment.ts +195 -86
- package/src/api/API.ts +14 -13
- package/src/api/APIOptions.ts +12 -11
- package/src/api/Alignment.ts +148 -92
- package/src/api/Common.ts +1 -1
- package/src/api/Denoising.ts +28 -28
- package/src/api/LanguageDetection.ts +135 -48
- package/src/api/Recognition.ts +198 -59
- package/src/api/SourceSeparation.ts +99 -0
- package/src/api/Synthesis.ts +217 -181
- package/src/api/Translation.ts +193 -40
- package/src/api/Vad.ts +110 -41
- package/src/audio/AudioBufferConversion.ts +4 -4
- package/src/audio/AudioPlayer.ts +27 -27
- package/src/audio/AudioRecorder.ts +5 -5
- package/src/audio/AudioUtilities.ts +107 -24
- package/src/cli/CLI.ts +313 -164
- package/src/cli/CLIConfigFile.ts +8 -8
- package/src/cli/CLILauncher.ts +6 -6
- package/src/cli/CLIOptionsSchema.ts +2 -2
- package/src/cli/CLIParser.ts +5 -5
- package/src/cli/CLIStarter.ts +4 -4
- package/src/codecs/FFMpegTranscoder.ts +38 -38
- package/src/codecs/TIMITCodec.ts +5 -5
- package/src/codecs/WaveCodec.ts +22 -22
- package/src/denoising/RNNoise.ts +9 -9
- package/src/dsp/BiquadFilter.ts +19 -11
- package/src/dsp/DecayingPeakEstimator.ts +35 -0
- package/src/dsp/FFT.ts +103 -35
- package/src/dsp/KWeightingFilter.ts +43 -0
- package/src/dsp/LoudnessEstimator.ts +74 -0
- package/src/dsp/MFCC.ts +15 -15
- package/src/dsp/MelSpectogram.ts +7 -7
- package/src/dsp/Rubberband.ts +38 -38
- package/src/dsp/Sonic.ts +4 -4
- package/src/dsp/SpeexResampler.ts +2 -2
- package/src/math/VectorMath.ts +42 -33
- package/src/nlp/ChineseSegmentation.ts +3 -3
- package/src/nlp/CompromiseNLP.ts +3 -3
- package/src/nlp/EspeakPhonemizer.ts +30 -30
- package/src/nlp/IPA.ts +20 -20
- package/src/nlp/JapaneseSegmentation.ts +6 -6
- package/src/nlp/Lexicon.ts +8 -8
- package/src/nlp/Segmentation.ts +23 -14
- package/src/nlp/TextNormalizer.ts +16 -16
- package/src/recognition/AmazonTranscribeSTT.ts +16 -17
- package/src/recognition/AzureCognitiveServicesSTT.ts +8 -6
- package/src/recognition/GoogleCloudSTT.ts +21 -21
- package/src/recognition/OpenAICloudSTT.ts +142 -0
- package/src/recognition/SileroSTT.ts +26 -26
- package/src/recognition/VoskSTT.ts +10 -10
- package/src/recognition/WhisperCppSTT.ts +555 -0
- package/src/recognition/WhisperSTT.ts +760 -507
- package/src/server/Client.ts +23 -23
- package/src/server/Server.ts +9 -9
- package/src/server/Worker.ts +53 -53
- package/src/server/WorkerStarter.ts +2 -2
- package/src/source-separation/MDXNetSourceSeparation.ts +228 -0
- package/src/speech-language-detection/SileroLanguageDetection.ts +8 -8
- package/src/subtitles/Subtitles.ts +3 -3
- package/src/synthesis/AwsPollyTTS.ts +14 -14
- package/src/synthesis/AzureCognitiveServicesTTS.ts +10 -10
- package/src/synthesis/CoquiServerTTS.ts +10 -10
- package/src/synthesis/ElevenlabsTTS.ts +137 -0
- package/src/synthesis/EspeakTTS.ts +90 -71
- package/src/synthesis/FliteTTS.ts +157 -157
- package/src/synthesis/GoogleCloudTTS.ts +19 -19
- package/src/synthesis/GoogleTranslateTTS.ts +104 -104
- package/src/synthesis/MicrosoftEdgeTTS.ts +80 -80
- package/src/synthesis/OpenAICloudTTS.ts +196 -0
- package/src/synthesis/SamTTS.ts +3 -3
- package/src/synthesis/SapiTTS.ts +29 -29
- package/src/synthesis/StreamlabsPollyTTS.ts +29 -29
- package/src/synthesis/SvoxPicoTTS.ts +67 -67
- package/src/synthesis/VitsTTS.ts +380 -380
- package/src/tests/Test.ts +4 -4
- package/src/utilities/Compression.ts +34 -13
- package/src/utilities/FileDownloader.ts +19 -19
- package/src/utilities/FileSystem.ts +7 -7
- package/src/utilities/Locale.ts +22 -22
- package/src/utilities/Logger.ts +4 -4
- package/src/utilities/ObjectUtilities.ts +19 -19
- package/src/utilities/OpenPromise.ts +2 -2
- package/src/utilities/PackageManager.ts +40 -0
- package/src/utilities/PathUtilities.ts +8 -8
- package/src/utilities/RandomGenerator.ts +3 -3
- package/src/utilities/SmoothEstimator.ts +35 -0
- package/src/utilities/TarballMaker.ts +9 -9
- package/src/utilities/Timeline.ts +15 -13
- package/src/utilities/Timer.ts +4 -4
- package/src/utilities/Utilities.ts +49 -15
- package/src/utilities/WasmMemoryManager.ts +7 -7
- package/src/utilities/WebReader.ts +23 -23
- package/src/utilities/WikipediaReader.ts +2 -2
- package/src/voice-activity-detection/AdaptiveGateVAD.ts +202 -0
- package/src/voice-activity-detection/SileroVAD.ts +5 -5
- package/src/voice-activity-detection/WebRtcVAD.ts +5 -5
- package/dist/synthesis/ElevenLabsTTS.d.ts +0 -8
- package/dist/synthesis/ElevenLabsTTS.js +0 -82
- package/dist/synthesis/ElevenLabsTTS.js.map +0 -1
- package/src/synthesis/ElevenLabsTTS.ts +0 -104
package/docs/Technical.md
CHANGED
|
@@ -16,7 +16,7 @@ Packages are downloaded as `.tar.gz` files, and are extracted to `[data-folder]/
|
|
|
16
16
|
* `Users/User/Library/Application Support/echogarden` on macOS
|
|
17
17
|
* `/home/user/.local/share/echogarden` on Linux
|
|
18
18
|
|
|
19
|
-
By using downloadable packages, the installed size is made significantly smaller and the installation faster. The total size of all available packages is currently about
|
|
19
|
+
By using downloadable packages, the installed size is made significantly smaller and the installation faster. The total size of all available packages is currently about 50GB (may not be up-to-date).
|
|
20
20
|
|
|
21
21
|
The packages are currently hosted and downloaded from a dedicated [Hugging Face repository](https://huggingface.co/echogarden/echogarden-packages).
|
|
22
22
|
|
|
@@ -24,14 +24,14 @@ The packages are currently hosted and downloaded from a dedicated [Hugging Face
|
|
|
24
24
|
|
|
25
25
|
The base installed (uncompressed) size, including dependencies, is around 270MB. This is without any models or voices, which are downloaded as needed.
|
|
26
26
|
|
|
27
|
-
Currently, the
|
|
27
|
+
Currently, the largest contributors to the size are:
|
|
28
28
|
|
|
29
29
|
* `onnxruntime-node` (NAPI): 92MB
|
|
30
30
|
* `kuromoji` (JavaScript) 40MB
|
|
31
31
|
* `flite-wasi` (WASI): 20MB
|
|
32
32
|
* `espeak-ng-emscripten` (WASM): 18MB
|
|
33
33
|
|
|
34
|
-
`onnxruntime-node` is
|
|
34
|
+
`onnxruntime-node` is large because it bundles pre-compiled binaries for multiple platforms. `kuromoji` is large because of its dictionary files and some unessential test code it bundles. The other three packages include large WASM binaries.
|
|
35
35
|
|
|
36
36
|
So, yes, in the future it may be possible to reduce the core installed size by dynamically installing some of these dependencies, or using modified, "slimmed-down" versions of some packages.
|
|
37
37
|
|
|
@@ -51,7 +51,7 @@ However, it is a lot of work, and only a subset of the engines can be supported
|
|
|
51
51
|
* Possibly lots of issues with inconsistent browser support and browser security constraints
|
|
52
52
|
* Not future-proof. Due to changing restrictions of browsers, the runtime environment is not guaranteed be reliably reproducible in the future, meaning that it may need continuous maintenance to ensure it keeps working on the newest browsers
|
|
53
53
|
|
|
54
|
-
It remains to be seen if this sort of work would feel justified somehow. I designed the tool to make the local installation extremely easy and issue-free. I guess it could look "impressive" to be able to run it in a browser, and may
|
|
54
|
+
It remains to be seen if this sort of work would feel justified somehow. I designed the tool to make the local installation extremely easy and issue-free. I guess it could look "impressive" to be able to run it in a browser, and may become as a nice "toy" or "tech-demo", and could get some attention, but it may eventually turn out to be simpler and more practical to just install a local instance and connect to it from the browser via a WebSocket API.
|
|
55
55
|
|
|
56
56
|
A TTS-only browser extension is in development. It registers Echogarden's voices on the browser's Web Speech API using the [`chrome.ttsEngine`](https://developer.chrome.com/docs/extensions/reference/ttsEngine/) extension API and communicates with it using the WebSocket API
|
|
57
57
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "echogarden",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "An integrated speech toolset, providing a variety of synthesis, recognition, alignment, and other processing tools, designed to be directly accessible to end-users.",
|
|
5
5
|
"author": "Rotem Dan",
|
|
6
6
|
"license": "GPL-3.0",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"url": "https://github.com/echogarden-project/echogarden/issues"
|
|
25
25
|
},
|
|
26
26
|
"engines": {
|
|
27
|
-
"node": ">=
|
|
27
|
+
"node": ">=18"
|
|
28
28
|
},
|
|
29
29
|
"os": [
|
|
30
30
|
"win32",
|
|
@@ -54,8 +54,8 @@
|
|
|
54
54
|
"echogarden": "./dist/cli/CLILauncher.js"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@aws-sdk/client-polly": "^3.
|
|
58
|
-
"@aws-sdk/client-transcribe-streaming": "^3.
|
|
57
|
+
"@aws-sdk/client-polly": "^3.554.0",
|
|
58
|
+
"@aws-sdk/client-transcribe-streaming": "^3.554.0",
|
|
59
59
|
"@echogarden/espeak-ng-emscripten": "^0.1.2",
|
|
60
60
|
"@echogarden/fasttext-wasm": "^0.1.0",
|
|
61
61
|
"@echogarden/flite-wasi": "^0.1.1",
|
|
@@ -72,10 +72,10 @@
|
|
|
72
72
|
"chalk": "^5.3.0",
|
|
73
73
|
"cldr-segmentation": "^2.2.0",
|
|
74
74
|
"command-exists": "^1.2.9",
|
|
75
|
-
"compromise": "^14.
|
|
75
|
+
"compromise": "^14.13.0",
|
|
76
76
|
"compromise-dates": "^3.5.0",
|
|
77
77
|
"fs-extra": "^11.2.0",
|
|
78
|
-
"gaxios": "^6.
|
|
78
|
+
"gaxios": "^6.4.0",
|
|
79
79
|
"graceful-fs": "^4.2.11",
|
|
80
80
|
"html-escaper": "^3.0.3",
|
|
81
81
|
"html-to-text": "^9.0.5",
|
|
@@ -90,9 +90,11 @@
|
|
|
90
90
|
"ndarray": "^1.0.19",
|
|
91
91
|
"ndarray-ops": "^1.2.2",
|
|
92
92
|
"onnxruntime-node": "^1.17.0",
|
|
93
|
+
"openai": "^4.33.0",
|
|
93
94
|
"sam-js": "^0.2.1",
|
|
94
95
|
"strip-ansi": "^7.1.0",
|
|
95
|
-
"tar": "^
|
|
96
|
+
"tar": "^7.0.0",
|
|
97
|
+
"tiktoken": "^1.0.13",
|
|
96
98
|
"tinyld": "^1.3.4",
|
|
97
99
|
"ws": "^8.16.0",
|
|
98
100
|
"wtf_wikipedia": "^10.3.0"
|
|
@@ -121,14 +123,11 @@
|
|
|
121
123
|
"@types/msgpack-lite": "^0.1.11",
|
|
122
124
|
"@types/ndarray": "^1.0.14",
|
|
123
125
|
"@types/ndarray-ops": "^1.2.7",
|
|
124
|
-
"@types/node": "^20.
|
|
126
|
+
"@types/node": "^20.12.7",
|
|
125
127
|
"@types/recursive-readdir": "^2.2.4",
|
|
126
|
-
"@types/tar": "^6.1.
|
|
128
|
+
"@types/tar": "^6.1.12",
|
|
127
129
|
"@types/ws": "^8.5.10",
|
|
128
|
-
"
|
|
129
|
-
"
|
|
130
|
-
"eslint": "^8.57.0",
|
|
131
|
-
"ts-json-schema-generator": "^1.5.0",
|
|
132
|
-
"typescript": "^5.4.2"
|
|
130
|
+
"ts-json-schema-generator": "^1.5.1",
|
|
131
|
+
"typescript": "^5.4.5"
|
|
133
132
|
}
|
|
134
133
|
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { cosineDistancePrecomputedMagnitudes, createVectorForIntegerRange,
|
|
2
|
-
import { logToStderr } from
|
|
3
|
-
import { alignDTWWindowed } from
|
|
1
|
+
import { cosineDistancePrecomputedMagnitudes, createVectorForIntegerRange, euclidianDistance, magnitude } from '../math/VectorMath.js'
|
|
2
|
+
import { logToStderr } from '../utilities/Utilities.js'
|
|
3
|
+
import { alignDTWWindowed } from './DTWSequenceAlignmentWindowed.js'
|
|
4
4
|
|
|
5
5
|
const log = logToStderr
|
|
6
6
|
|
|
7
|
-
export async function alignMFCC_DTW(mfccFrames1: number[][], mfccFrames2: number[][], windowLength: number, distanceFunction:
|
|
8
|
-
if (distanceFunction ==
|
|
7
|
+
export async function alignMFCC_DTW(mfccFrames1: number[][], mfccFrames2: number[][], windowLength: number, distanceFunction: 'euclidian' | 'cosine' = 'euclidian', centerIndexes?: number[]) {
|
|
8
|
+
if (distanceFunction == 'euclidian') {
|
|
9
9
|
const { path } = alignDTWWindowed(
|
|
10
10
|
mfccFrames1,
|
|
11
11
|
mfccFrames2,
|
|
12
|
-
|
|
12
|
+
euclidianDistance,
|
|
13
13
|
windowLength,
|
|
14
14
|
centerIndexes
|
|
15
15
|
)
|
|
16
16
|
|
|
17
17
|
return path
|
|
18
|
-
} else if (distanceFunction ==
|
|
18
|
+
} else if (distanceFunction == 'cosine') {
|
|
19
19
|
const indexes1 = createVectorForIntegerRange(0, mfccFrames1.length)
|
|
20
20
|
const indexes2 = createVectorForIntegerRange(0, mfccFrames2.length)
|
|
21
21
|
|
|
@@ -32,12 +32,12 @@ export async function alignMFCC_DTW(mfccFrames1: number[][], mfccFrames2: number
|
|
|
32
32
|
|
|
33
33
|
return path
|
|
34
34
|
} else {
|
|
35
|
-
throw new Error(
|
|
35
|
+
throw new Error('Invalid distance function')
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
export function getCostMatrixMemorySizeMB(sequence1Length: number, sequence2Length: number, windowLength: number) {
|
|
40
40
|
const costMatrixMemorySizeMB = sequence1Length * Math.min(sequence2Length, windowLength) * 4 / 1000000
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
return costMatrixMemorySizeMB
|
|
43
43
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { logToStderr } from
|
|
2
|
-
import { AlignmentPath } from
|
|
1
|
+
import { logToStderr } from '../utilities/Utilities.js'
|
|
2
|
+
import { AlignmentPath } from './SpeechAlignment.js'
|
|
3
3
|
|
|
4
4
|
const log = logToStderr
|
|
5
5
|
|
|
6
6
|
export function alignDTWWindowed<T, U>(sequence1: T[], sequence2: U[], costFunction: (a: T, b: U) => number, windowMaxLength: number, centerIndexes?: number[]) {
|
|
7
7
|
if (windowMaxLength < 2) {
|
|
8
|
-
throw new Error(
|
|
8
|
+
throw new Error('Window length must be greater or equal to 2')
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
if (sequence1.length == 0 || sequence2.length == 0) {
|
|
@@ -1,16 +1,26 @@
|
|
|
1
|
-
import { clip } from
|
|
1
|
+
import { clip } from '../utilities/Utilities.js'
|
|
2
|
+
|
|
3
|
+
import * as API from '../api/API.js'
|
|
4
|
+
|
|
5
|
+
import { computeMFCCs, extendDefaultMfccOptions, MfccOptions } from '../dsp/MFCC.js'
|
|
6
|
+
import { alignMFCC_DTW, getCostMatrixMemorySizeMB } from './DTWMfccSequenceAlignment.js'
|
|
7
|
+
import { Logger } from '../utilities/Logger.js'
|
|
8
|
+
import { Timeline, TimelineEntry } from '../utilities/Timeline.js'
|
|
9
|
+
import { downmixToMonoAndNormalize, getEndingSilentSampleCount, getRawAudioDuration, getStartingSilentSampleCount, RawAudio } from '../audio/AudioUtilities.js'
|
|
10
|
+
import chalk from 'chalk'
|
|
11
|
+
import { synthesize } from '../api/API.js'
|
|
12
|
+
import { resampleAudioSpeex } from '../dsp/SpeexResampler.js'
|
|
13
|
+
import { deepClone } from '../utilities/ObjectUtilities.js'
|
|
14
|
+
import { zeroIfNaN } from '../math/VectorMath.js'
|
|
15
|
+
import { EspeakOptions } from '../synthesis/EspeakTTS.js'
|
|
16
|
+
|
|
17
|
+
export async function alignUsingDtw(
|
|
18
|
+
sourceRawAudio: RawAudio,
|
|
19
|
+
referenceRawAudio: RawAudio,
|
|
20
|
+
referenceTimeline: Timeline,
|
|
21
|
+
granularities: DtwGranularity[],
|
|
22
|
+
windowDurations: number[]) {
|
|
2
23
|
|
|
3
|
-
import * as API from "../api/API.js"
|
|
4
|
-
|
|
5
|
-
import { computeMFCCs, extendDefaultMfccOptions, MfccOptions } from "../dsp/MFCC.js"
|
|
6
|
-
import { alignMFCC_DTW, getCostMatrixMemorySizeMB } from "./DTWMfccSequenceAlignment.js"
|
|
7
|
-
import { Logger } from "../utilities/Logger.js"
|
|
8
|
-
import { Timeline, TimelineEntry } from "../utilities/Timeline.js"
|
|
9
|
-
import { getEndingSilentSampleCount, getRawAudioDuration, getStartingSilentSampleCount, RawAudio } from "../audio/AudioUtilities.js"
|
|
10
|
-
import { type EspeakOptions } from "../synthesis/EspeakTTS.js"
|
|
11
|
-
import chalk from "chalk"
|
|
12
|
-
|
|
13
|
-
export async function alignUsingDtw(sourceRawAudio: RawAudio, referenceRawAudio: RawAudio, referenceTimeline: Timeline, granularities: DtwGranularity[], windowDurations: number[]) {
|
|
14
24
|
const logger = new Logger()
|
|
15
25
|
|
|
16
26
|
if (windowDurations.length == 0) {
|
|
@@ -38,11 +48,11 @@ export async function alignUsingDtw(sourceRawAudio: RawAudio, referenceRawAudio:
|
|
|
38
48
|
framesPerSecond = 1 / mfccOptions.hopDuration!
|
|
39
49
|
|
|
40
50
|
// Compute reference MFCCs
|
|
41
|
-
logger.start(
|
|
51
|
+
logger.start('Compute reference MFCC features')
|
|
42
52
|
const referenceMfccs = await computeMFCCs(referenceRawAudio, mfccOptions)
|
|
43
53
|
|
|
44
54
|
// Compute source MFCCs
|
|
45
|
-
logger.start(
|
|
55
|
+
logger.start('Compute source MFCC features')
|
|
46
56
|
const sourceMfccs = await computeMFCCs(sourceRawAudio, mfccOptions)
|
|
47
57
|
logger.end()
|
|
48
58
|
|
|
@@ -52,12 +62,12 @@ export async function alignUsingDtw(sourceRawAudio: RawAudio, referenceRawAudio:
|
|
|
52
62
|
if (passIndex == 0) {
|
|
53
63
|
const minRecommendedWindowDuration = 0.2 * rawAudioDuration
|
|
54
64
|
|
|
55
|
-
if (windowDuration < minRecommendedWindowDuration
|
|
65
|
+
if (windowDuration < minRecommendedWindowDuration) {
|
|
56
66
|
logger.logTitledMessage('Warning', `Maximum DTW window duration is set to ${windowDuration.toFixed(1)}s, which is smaller than 20% of the source audio duration of ${rawAudioDuration.toFixed(1)}s. This may lead to suboptimal results in some cases. Consider increasing window duration if needed.`, chalk.yellowBright)
|
|
57
67
|
}
|
|
58
68
|
}
|
|
59
69
|
|
|
60
|
-
logger.start(
|
|
70
|
+
logger.start('Align MFCC features using DTW')
|
|
61
71
|
const dtwWindowLength = Math.floor(windowDuration * framesPerSecond)
|
|
62
72
|
|
|
63
73
|
let centerIndexes: number[] | undefined
|
|
@@ -85,18 +95,18 @@ export async function alignUsingDtw(sourceRawAudio: RawAudio, referenceRawAudio:
|
|
|
85
95
|
logger.end()
|
|
86
96
|
}
|
|
87
97
|
|
|
88
|
-
logger.start(
|
|
98
|
+
logger.start('\nConvert path to timeline')
|
|
89
99
|
|
|
90
100
|
function getMappedTimelineEntry(timelineEntry: TimelineEntry, recurse = true): TimelineEntry {
|
|
91
101
|
const referenceStartFrameIndex = Math.floor(timelineEntry.startTime * framesPerSecond)
|
|
92
102
|
const referenceEndFrameIndex = Math.floor(timelineEntry.endTime * framesPerSecond)
|
|
93
103
|
|
|
94
104
|
if (referenceStartFrameIndex < 0 || referenceEndFrameIndex < 0) {
|
|
95
|
-
throw new Error(
|
|
105
|
+
throw new Error('Unexpected: encountered a negative timestamp in timeline')
|
|
96
106
|
}
|
|
97
107
|
|
|
98
|
-
const mappedStartFrameIndex = getMappedFrameIndexForPath(referenceStartFrameIndex, compactedPath,
|
|
99
|
-
const mappedEndFrameIndex = getMappedFrameIndexForPath(referenceEndFrameIndex, compactedPath,
|
|
108
|
+
const mappedStartFrameIndex = getMappedFrameIndexForPath(referenceStartFrameIndex, compactedPath, 'first')
|
|
109
|
+
const mappedEndFrameIndex = getMappedFrameIndexForPath(referenceEndFrameIndex, compactedPath, 'first')
|
|
100
110
|
|
|
101
111
|
let innerTimeline: Timeline | undefined
|
|
102
112
|
|
|
@@ -141,7 +151,16 @@ export async function alignUsingDtw(sourceRawAudio: RawAudio, referenceRawAudio:
|
|
|
141
151
|
return mappedTimeline
|
|
142
152
|
}
|
|
143
153
|
|
|
144
|
-
export async function
|
|
154
|
+
export async function alignUsingDtwWithRecognitionReference(
|
|
155
|
+
sourceRawAudio: RawAudio,
|
|
156
|
+
referenceRawAudio: RawAudio,
|
|
157
|
+
referenceTimeline: Timeline,
|
|
158
|
+
recognitionTimeline: Timeline,
|
|
159
|
+
granularities: DtwGranularity[],
|
|
160
|
+
windowDurations: number[],
|
|
161
|
+
espeakOptions: EspeakOptions,
|
|
162
|
+
phoneAlignmentMethod: API.PhoneAlignmentMethod = 'interpolation') {
|
|
163
|
+
|
|
145
164
|
const logger = new Logger()
|
|
146
165
|
|
|
147
166
|
if (recognitionTimeline.length == 0) {
|
|
@@ -167,57 +186,89 @@ export async function alignUsingDtwWithRecognition(sourceRawAudio: RawAudio, ref
|
|
|
167
186
|
logger.start("Synthesize recognized transcript with eSpeak")
|
|
168
187
|
const recognizedWords = recognitionTimeline.map(entry => entry.text)
|
|
169
188
|
|
|
170
|
-
const {
|
|
189
|
+
const {
|
|
190
|
+
rawAudio: synthesizedRecognizedTranscriptRawAudio,
|
|
191
|
+
timeline: synthesizedRecognitionTimeline
|
|
192
|
+
} = await createAlignmentReferenceUsingEspeakForFragments(recognizedWords, espeakOptions)
|
|
171
193
|
|
|
172
194
|
let recognitionTimelineWithPhones: Timeline
|
|
173
195
|
|
|
174
|
-
if (phoneAlignmentMethod ==
|
|
196
|
+
if (phoneAlignmentMethod == 'interpolation') {
|
|
175
197
|
// Add phone timelines by interpolating from reference words
|
|
176
|
-
logger.start(
|
|
198
|
+
logger.start('Interpolate phone timing')
|
|
177
199
|
|
|
178
|
-
recognitionTimelineWithPhones = await interpolatePhoneTimelines(
|
|
179
|
-
|
|
180
|
-
|
|
200
|
+
recognitionTimelineWithPhones = await interpolatePhoneTimelines(
|
|
201
|
+
recognitionTimeline,
|
|
202
|
+
synthesizedRecognitionTimeline
|
|
203
|
+
)
|
|
204
|
+
} else if (phoneAlignmentMethod == 'dtw') {
|
|
205
|
+
logger.start('Align phone timing')
|
|
181
206
|
|
|
182
207
|
// Add phone timelines by aligning each individual recognized word with the corresponding word
|
|
183
208
|
// in the reference timeline
|
|
184
|
-
recognitionTimelineWithPhones = await alignPhoneTimelines(
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
209
|
+
recognitionTimelineWithPhones = await alignPhoneTimelines(
|
|
210
|
+
sourceRawAudio,
|
|
211
|
+
recognitionTimeline,
|
|
212
|
+
synthesizedRecognizedTranscriptRawAudio,
|
|
213
|
+
synthesizedRecognitionTimeline,
|
|
214
|
+
60)
|
|
188
215
|
} else {
|
|
189
216
|
throw new Error(`Unknown phone alignment method: ${phoneAlignmentMethod}`)
|
|
190
217
|
}
|
|
191
218
|
|
|
192
|
-
logger.start("Map from the synthesized recognized timeline to the recognized timeline")
|
|
193
219
|
// Create a mapping from the synthesized recognized timeline to the recognized timeline
|
|
220
|
+
logger.start("Map from the synthesized recognized timeline to the recognized timeline")
|
|
221
|
+
|
|
222
|
+
type SynthesizedToRecognizedTimeMappingEntry = {
|
|
223
|
+
synthesized: number
|
|
224
|
+
recognized: number
|
|
225
|
+
}
|
|
226
|
+
|
|
194
227
|
type SynthesizedToRecognizedTimeMapping = SynthesizedToRecognizedTimeMappingEntry[]
|
|
195
|
-
type SynthesizedToRecognizedTimeMappingEntry = { synthesized: number, recognized: number }
|
|
196
228
|
|
|
197
229
|
const synthesizedToRecognizedTimeMapping: SynthesizedToRecognizedTimeMapping = []
|
|
198
230
|
|
|
199
|
-
for (let
|
|
200
|
-
const synthesizedTimelineEntry = synthesizedRecognitionTimeline[
|
|
201
|
-
const recognitionTimelineEntry = recognitionTimelineWithPhones[
|
|
231
|
+
for (let wordEntryIndex = 0; wordEntryIndex < synthesizedRecognitionTimeline.length; wordEntryIndex++) {
|
|
232
|
+
const synthesizedTimelineEntry = synthesizedRecognitionTimeline[wordEntryIndex]
|
|
233
|
+
const recognitionTimelineEntry = recognitionTimelineWithPhones[wordEntryIndex]
|
|
202
234
|
|
|
203
|
-
synthesizedToRecognizedTimeMapping.push({
|
|
235
|
+
synthesizedToRecognizedTimeMapping.push({
|
|
236
|
+
synthesized: synthesizedTimelineEntry.startTime,
|
|
237
|
+
recognized: recognitionTimelineEntry.startTime
|
|
238
|
+
})
|
|
204
239
|
|
|
205
240
|
if (synthesizedTimelineEntry.timeline) {
|
|
206
|
-
for (let
|
|
207
|
-
const synthesizedPhoneTimelineEntry = synthesizedTimelineEntry.timeline[
|
|
208
|
-
const recognitionPhoneTimelineEntry = recognitionTimelineEntry.timeline![
|
|
209
|
-
|
|
210
|
-
synthesizedToRecognizedTimeMapping.push({
|
|
211
|
-
|
|
241
|
+
for (let tokenEntryIndex = 0; tokenEntryIndex < synthesizedTimelineEntry.timeline.length; tokenEntryIndex++) {
|
|
242
|
+
const synthesizedPhoneTimelineEntry = synthesizedTimelineEntry.timeline[tokenEntryIndex]
|
|
243
|
+
const recognitionPhoneTimelineEntry = recognitionTimelineEntry.timeline![tokenEntryIndex]
|
|
244
|
+
|
|
245
|
+
synthesizedToRecognizedTimeMapping.push({
|
|
246
|
+
synthesized: synthesizedPhoneTimelineEntry.startTime,
|
|
247
|
+
recognized: recognitionPhoneTimelineEntry.startTime
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
synthesizedToRecognizedTimeMapping.push({
|
|
251
|
+
synthesized: synthesizedPhoneTimelineEntry.endTime,
|
|
252
|
+
recognized: recognitionPhoneTimelineEntry.endTime
|
|
253
|
+
})
|
|
212
254
|
}
|
|
213
255
|
}
|
|
214
256
|
|
|
215
|
-
synthesizedToRecognizedTimeMapping.push({
|
|
257
|
+
synthesizedToRecognizedTimeMapping.push({
|
|
258
|
+
synthesized: synthesizedTimelineEntry.endTime,
|
|
259
|
+
recognized: recognitionTimelineEntry.endTime
|
|
260
|
+
})
|
|
216
261
|
}
|
|
217
262
|
|
|
218
|
-
logger.start("Align the synthesized recognized transcript with the synthesized ground-truth transcript")
|
|
219
263
|
// Align the synthesized recognized transcript to the synthesized reference transcript
|
|
220
|
-
|
|
264
|
+
logger.start("Align the synthesized recognized transcript with the synthesized ground-truth transcript")
|
|
265
|
+
|
|
266
|
+
const alignedSynthesizedRecognitionTimeline = await alignUsingDtw(
|
|
267
|
+
synthesizedRecognizedTranscriptRawAudio,
|
|
268
|
+
referenceRawAudio,
|
|
269
|
+
referenceTimeline,
|
|
270
|
+
granularities,
|
|
271
|
+
windowDurations)
|
|
221
272
|
|
|
222
273
|
let currentSynthesizedToRecognizedMappingIndex = 0
|
|
223
274
|
|
|
@@ -234,7 +285,7 @@ export async function alignUsingDtwWithRecognition(sourceRawAudio: RawAudio, ref
|
|
|
234
285
|
}
|
|
235
286
|
|
|
236
287
|
if (left > right) {
|
|
237
|
-
throw new Error("
|
|
288
|
+
throw new Error("Left is larger than right!")
|
|
238
289
|
}
|
|
239
290
|
|
|
240
291
|
if (Math.abs(synthesizedTime - left) < Math.abs(synthesizedTime - right)) {
|
|
@@ -273,44 +324,59 @@ export async function alignUsingDtwWithRecognition(sourceRawAudio: RawAudio, ref
|
|
|
273
324
|
export async function interpolatePhoneTimelines(sourceTimeline: Timeline, referenceTimeline: Timeline) {
|
|
274
325
|
const interpolatedTimeline: Timeline = []
|
|
275
326
|
|
|
276
|
-
for (let
|
|
277
|
-
const referenceEntry = referenceTimeline[
|
|
327
|
+
for (let wordEntryIndex = 0; wordEntryIndex < sourceTimeline.length; wordEntryIndex++) {
|
|
328
|
+
const referenceEntry = referenceTimeline[wordEntryIndex]
|
|
278
329
|
|
|
279
|
-
const interpolatedEntry =
|
|
330
|
+
const interpolatedEntry = deepClone(sourceTimeline[wordEntryIndex])
|
|
280
331
|
interpolatedTimeline.push(interpolatedEntry)
|
|
281
332
|
|
|
282
|
-
if (interpolatedEntry.type !=
|
|
333
|
+
if (interpolatedEntry.type != 'word') {
|
|
283
334
|
continue
|
|
284
335
|
}
|
|
285
336
|
|
|
286
337
|
const interpolatedEntryDuration = interpolatedEntry.endTime - interpolatedEntry.startTime
|
|
287
338
|
const synthesisEntryDuration = referenceEntry.endTime - referenceEntry.startTime
|
|
288
339
|
|
|
289
|
-
|
|
340
|
+
function mapEntry(targetEntry: TimelineEntry): TimelineEntry {
|
|
341
|
+
const targetStartTimePercentageRelativeToWord =
|
|
342
|
+
(targetEntry.startTime - referenceEntry.startTime) / synthesisEntryDuration
|
|
290
343
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
(phoneEntry.startTime - referenceEntry.startTime) / synthesisEntryDuration
|
|
344
|
+
const targetEndTimePercentageRelativeToWord =
|
|
345
|
+
(targetEntry.endTime - referenceEntry.startTime) / synthesisEntryDuration
|
|
294
346
|
|
|
295
|
-
const
|
|
296
|
-
|
|
347
|
+
const interpolatedStartTime =
|
|
348
|
+
interpolatedEntry.startTime + (zeroIfNaN(targetStartTimePercentageRelativeToWord) * interpolatedEntryDuration)
|
|
297
349
|
|
|
298
|
-
const
|
|
299
|
-
|
|
350
|
+
const interpolatedEndTime =
|
|
351
|
+
interpolatedEntry.startTime + (zeroIfNaN(targetEndTimePercentageRelativeToWord) * interpolatedEntryDuration)
|
|
300
352
|
|
|
301
|
-
|
|
302
|
-
...
|
|
353
|
+
return {
|
|
354
|
+
...targetEntry,
|
|
303
355
|
|
|
304
|
-
startTime:
|
|
305
|
-
endTime:
|
|
306
|
-
}
|
|
356
|
+
startTime: interpolatedStartTime,
|
|
357
|
+
endTime: interpolatedEndTime
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const interpolatedPhoneEntries: Timeline = []
|
|
362
|
+
|
|
363
|
+
for (const phoneEntry of (referenceEntry.timeline || [])) {
|
|
364
|
+
interpolatedPhoneEntries.push(mapEntry(phoneEntry))
|
|
307
365
|
}
|
|
366
|
+
|
|
367
|
+
interpolatedEntry.timeline = interpolatedPhoneEntries
|
|
308
368
|
}
|
|
309
369
|
|
|
310
370
|
return interpolatedTimeline
|
|
311
371
|
}
|
|
312
372
|
|
|
313
|
-
export async function alignPhoneTimelines(
|
|
373
|
+
export async function alignPhoneTimelines(
|
|
374
|
+
sourceRawAudio: RawAudio,
|
|
375
|
+
sourceWordTimeline: Timeline,
|
|
376
|
+
referenceRawAudio: RawAudio,
|
|
377
|
+
referenceTimeline: Timeline,
|
|
378
|
+
windowDuration: number) {
|
|
379
|
+
|
|
314
380
|
const mfccOptions: MfccOptions = extendDefaultMfccOptions({ zeroFirstCoefficient: true })
|
|
315
381
|
|
|
316
382
|
const framesPerSecond = 1 / mfccOptions.hopDuration!
|
|
@@ -323,10 +389,9 @@ export async function alignPhoneTimelines(sourceRawAudio: RawAudio, sourceWordTi
|
|
|
323
389
|
for (let i = 0; i < referenceTimeline.length; i++) {
|
|
324
390
|
const referenceWordEntry = referenceTimeline[i]
|
|
325
391
|
|
|
326
|
-
const alignedWordEntry =
|
|
327
|
-
alignedWordTimeline.push(alignedWordEntry)
|
|
392
|
+
const alignedWordEntry = deepClone(sourceWordTimeline[i])
|
|
328
393
|
|
|
329
|
-
if (alignedWordEntry.type !=
|
|
394
|
+
if (alignedWordEntry.type != 'word') {
|
|
330
395
|
continue
|
|
331
396
|
}
|
|
332
397
|
|
|
@@ -351,27 +416,36 @@ export async function alignPhoneTimelines(sourceRawAudio: RawAudio, sourceWordTi
|
|
|
351
416
|
const sourceWordMfccs = sourceMfccs.slice(alignedWordStartFrameIndex, alignedWordEndFrameIndex)
|
|
352
417
|
|
|
353
418
|
// Compute DTW path
|
|
354
|
-
const rawPath = await alignMFCC_DTW(referenceWordMfccs, sourceWordMfccs,
|
|
419
|
+
const rawPath = await alignMFCC_DTW(referenceWordMfccs, sourceWordMfccs, windowDuration * framesPerSecond)
|
|
355
420
|
const compactedPath = compactPath(rawPath)
|
|
356
421
|
|
|
357
|
-
|
|
358
|
-
|
|
422
|
+
function mapEntry(referenceEntry: TimelineEntry): TimelineEntry {
|
|
423
|
+
const referenceStartFrameOffset = Math.floor((referenceEntry.startTime - referenceWordEntry.startTime) * framesPerSecond)
|
|
424
|
+
const alignedStartFrameOffset = getMappedFrameIndexForPath(referenceStartFrameOffset, compactedPath)
|
|
425
|
+
const alignedStartTime = alignedWordEntry.startTime + (alignedStartFrameOffset / framesPerSecond)
|
|
359
426
|
|
|
360
|
-
|
|
361
|
-
const
|
|
362
|
-
const
|
|
363
|
-
const alignedPhoneStartTime = alignedWordEntry.startTime + (alignedPhoneStartFrameOffset / framesPerSecond)
|
|
427
|
+
const referenceEndFrameOffset = Math.floor((referenceEntry.endTime - referenceWordEntry.startTime) * framesPerSecond)
|
|
428
|
+
const alignedEndFrameOffset = getMappedFrameIndexForPath(referenceEndFrameOffset, compactedPath)
|
|
429
|
+
const alignedEndTime = alignedWordEntry.startTime + (alignedEndFrameOffset / framesPerSecond)
|
|
364
430
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
const alignedPhoneEndTime = alignedWordEntry.startTime + (alignedPhoneEndFrameOffset / framesPerSecond)
|
|
431
|
+
return {
|
|
432
|
+
...referenceEntry,
|
|
368
433
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
endTime: alignedPhoneEndTime
|
|
373
|
-
})
|
|
434
|
+
startTime: alignedStartTime,
|
|
435
|
+
endTime: alignedEndTime
|
|
436
|
+
}
|
|
374
437
|
}
|
|
438
|
+
|
|
439
|
+
// Add phone timeline using the mapped time information
|
|
440
|
+
const alignedPhoneTimeline: Timeline = []
|
|
441
|
+
|
|
442
|
+
for (const referencePhoneEntry of (referenceWordEntry.timeline || [])) {
|
|
443
|
+
alignedPhoneTimeline.push(mapEntry(referencePhoneEntry))
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
alignedWordEntry.timeline = alignedPhoneTimeline
|
|
447
|
+
|
|
448
|
+
alignedWordTimeline.push(alignedWordEntry)
|
|
375
449
|
}
|
|
376
450
|
|
|
377
451
|
return alignedWordTimeline
|
|
@@ -385,7 +459,7 @@ export async function createAlignmentReferenceUsingEspeakForFragments(fragments:
|
|
|
385
459
|
|
|
386
460
|
progressLogger.start("Create alignment reference with eSpeak")
|
|
387
461
|
|
|
388
|
-
const result = await Espeak.synthesizeFragments(fragments, espeakOptions
|
|
462
|
+
const result = await Espeak.synthesizeFragments(fragments, espeakOptions)
|
|
389
463
|
|
|
390
464
|
result.timeline = result.timeline.flatMap(clause => clause.timeline!)
|
|
391
465
|
|
|
@@ -398,6 +472,41 @@ export async function createAlignmentReferenceUsingEspeakForFragments(fragments:
|
|
|
398
472
|
return result
|
|
399
473
|
}
|
|
400
474
|
|
|
475
|
+
export async function createAlignmentReferenceUsingEspeak(transcript: string, language: string, plaintextOptions?: API.PlainTextOptions, customLexiconPaths?: string[], insertSeparators?: boolean) {
|
|
476
|
+
const logger = new Logger()
|
|
477
|
+
|
|
478
|
+
logger.start('Create alignment reference with eSpeak')
|
|
479
|
+
|
|
480
|
+
const synthesisOptions: API.SynthesisOptions = {
|
|
481
|
+
engine: 'espeak',
|
|
482
|
+
language,
|
|
483
|
+
|
|
484
|
+
plainText: plaintextOptions,
|
|
485
|
+
customLexiconPaths: customLexiconPaths,
|
|
486
|
+
|
|
487
|
+
espeak: {
|
|
488
|
+
useKlatt: false,
|
|
489
|
+
insertSeparators,
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
let {
|
|
494
|
+
audio: referenceRawAudio,
|
|
495
|
+
timeline: segmentTimeline,
|
|
496
|
+
voice: espeakVoice
|
|
497
|
+
} = await synthesize(transcript, synthesisOptions)
|
|
498
|
+
|
|
499
|
+
const sentenceTimeline = segmentTimeline.flatMap(entry => entry.timeline!)
|
|
500
|
+
const wordTimeline = sentenceTimeline.flatMap(entry => entry.timeline!)
|
|
501
|
+
|
|
502
|
+
referenceRawAudio = await resampleAudioSpeex(referenceRawAudio as RawAudio, 16000)
|
|
503
|
+
referenceRawAudio = downmixToMonoAndNormalize(referenceRawAudio)
|
|
504
|
+
|
|
505
|
+
logger.end()
|
|
506
|
+
|
|
507
|
+
return { referenceRawAudio, referenceTimeline: wordTimeline, espeakVoice }
|
|
508
|
+
}
|
|
509
|
+
|
|
401
510
|
function compactPath(path: AlignmentPath) {
|
|
402
511
|
const compactedPath: CompactedPath = []
|
|
403
512
|
|
|
@@ -414,7 +523,7 @@ function compactPath(path: AlignmentPath) {
|
|
|
414
523
|
return compactedPath
|
|
415
524
|
}
|
|
416
525
|
|
|
417
|
-
function getMappedFrameIndexForPath(referenceFrameIndex: number, compactedPath: CompactedPath, mappingKind:
|
|
526
|
+
function getMappedFrameIndexForPath(referenceFrameIndex: number, compactedPath: CompactedPath, mappingKind: 'first' | 'last' = 'first') {
|
|
418
527
|
if (compactedPath.length == 0) {
|
|
419
528
|
return 0
|
|
420
529
|
}
|
|
@@ -425,7 +534,7 @@ function getMappedFrameIndexForPath(referenceFrameIndex: number, compactedPath:
|
|
|
425
534
|
|
|
426
535
|
let mappedFrameIndex: number
|
|
427
536
|
|
|
428
|
-
if (mappingKind ==
|
|
537
|
+
if (mappingKind == 'first') {
|
|
429
538
|
mappedFrameIndex = compactedPathEntry.first
|
|
430
539
|
} else {
|
|
431
540
|
mappedFrameIndex = compactedPathEntry.last
|