echogarden 0.12.2 → 1.0.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/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 +9 -10
- package/dist/alignment/SpeechAlignment.js +136 -105
- 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 +88 -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 +93 -19
- package/docs/Engines.md +28 -16
- package/docs/Licenses.md +4 -1
- package/docs/Options.md +158 -78
- package/docs/Releases.md +262 -0
- package/docs/Server.md +7 -7
- package/docs/Tasklist.md +95 -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 +204 -119
- package/src/api/API.ts +14 -13
- package/src/api/APIOptions.ts +12 -11
- package/src/api/Alignment.ts +147 -90
- 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/src/api/Synthesis.ts
CHANGED
|
@@ -1,27 +1,29 @@
|
|
|
1
|
-
import path from
|
|
1
|
+
import path from 'node:path'
|
|
2
2
|
|
|
3
|
-
import { deepClone, extendDeep } from
|
|
3
|
+
import { deepClone, extendDeep } from '../utilities/ObjectUtilities.js'
|
|
4
4
|
|
|
5
|
-
import * as FFMpegTranscoder from
|
|
5
|
+
import * as FFMpegTranscoder from '../codecs/FFMpegTranscoder.js'
|
|
6
6
|
|
|
7
|
-
import { clip, convertHtmlToText, sha256AsHex, simplifyPunctuationCharacters, stringifyAndFormatJson, logToStderr, yieldToEventLoop, delay, runOperationWithRetries } from
|
|
8
|
-
import { RawAudio, concatAudioSegments, downmixToMono,
|
|
9
|
-
import { Logger } from
|
|
7
|
+
import { clip, convertHtmlToText, sha256AsHex, simplifyPunctuationCharacters, stringifyAndFormatJson, logToStderr, yieldToEventLoop, delay, runOperationWithRetries } from '../utilities/Utilities.js'
|
|
8
|
+
import { RawAudio, attenuateIfClipping, concatAudioSegments, downmixToMono, encodeRawAudioToWave, getSamplePeakDecibels, getEmptyRawAudio, getRawAudioDuration, normalizeAudioLevel, trimAudioEnd, trimAudioStart } from '../audio/AudioUtilities.js'
|
|
9
|
+
import { Logger } from '../utilities/Logger.js'
|
|
10
10
|
|
|
11
|
-
import { isWordOrSymbolWord, splitToParagraphs, splitToSentences } from
|
|
12
|
-
import { type RubberbandOptions } from
|
|
13
|
-
import { loadLexiconsForLanguage } from
|
|
11
|
+
import { isWordOrSymbolWord, splitToParagraphs, splitToSentences } from '../nlp/Segmentation.js'
|
|
12
|
+
import { type RubberbandOptions } from '../dsp/Rubberband.js'
|
|
13
|
+
import { loadLexiconsForLanguage } from '../nlp/Lexicon.js'
|
|
14
14
|
|
|
15
|
-
import * as API from
|
|
16
|
-
import { Timeline, TimelineEntry, addTimeOffsetToTimeline, multiplyTimelineByFactor } from
|
|
17
|
-
import { getAppDataDir, ensureDir, existsSync, isFileIsUpToDate, readAndParseJsonFile, writeFileSafe } from
|
|
18
|
-
import { formatLanguageCodeWithName, getShortLanguageCode, normalizeLanguageCode, defaultDialectForLanguageCode } from
|
|
19
|
-
import { loadPackage } from
|
|
20
|
-
import { EngineMetadata, appName } from
|
|
21
|
-
import { shouldCancelCurrentTask } from
|
|
22
|
-
import chalk from
|
|
23
|
-
import { SubtitlesConfig, defaultSubtitlesBaseConfig } from
|
|
24
|
-
import { type EspeakOptions } from
|
|
15
|
+
import * as API from './API.js'
|
|
16
|
+
import { Timeline, TimelineEntry, addTimeOffsetToTimeline, multiplyTimelineByFactor } from '../utilities/Timeline.js'
|
|
17
|
+
import { getAppDataDir, ensureDir, existsSync, isFileIsUpToDate, readAndParseJsonFile, writeFileSafe } from '../utilities/FileSystem.js'
|
|
18
|
+
import { formatLanguageCodeWithName, getShortLanguageCode, normalizeLanguageCode, defaultDialectForLanguageCode } from '../utilities/Locale.js'
|
|
19
|
+
import { loadPackage } from '../utilities/PackageManager.js'
|
|
20
|
+
import { EngineMetadata, appName } from './Common.js'
|
|
21
|
+
import { shouldCancelCurrentTask } from '../server/Worker.js'
|
|
22
|
+
import chalk from 'chalk'
|
|
23
|
+
import { SubtitlesConfig, defaultSubtitlesBaseConfig } from '../subtitles/Subtitles.js'
|
|
24
|
+
import { type EspeakOptions } from '../synthesis/EspeakTTS.js'
|
|
25
|
+
import { OpenAICloudTTSOptions, defaultOpenAICloudTTSOptions } from '../synthesis/OpenAICloudTTS.js'
|
|
26
|
+
import { ElevenlabsTTSOptions, defaultElevenlabsTTSOptions } from '../synthesis/ElevenlabsTTS.js'
|
|
25
27
|
|
|
26
28
|
const log = logToStderr
|
|
27
29
|
|
|
@@ -51,7 +53,7 @@ async function synthesizeSegments(segments: string[], options: SynthesisOptions,
|
|
|
51
53
|
options = extendDeep(defaultSynthesisOptions, options)
|
|
52
54
|
|
|
53
55
|
if (!options.language && !options.voice) {
|
|
54
|
-
logger.start(
|
|
56
|
+
logger.start('No language or voice specified. Detecting language')
|
|
55
57
|
|
|
56
58
|
let segmentsPlainText = segments
|
|
57
59
|
|
|
@@ -63,7 +65,7 @@ async function synthesizeSegments(segments: string[], options: SynthesisOptions,
|
|
|
63
65
|
}
|
|
64
66
|
}
|
|
65
67
|
|
|
66
|
-
const { detectedLanguage } = await API.detectTextLanguage(segmentsPlainText.join(
|
|
68
|
+
const { detectedLanguage } = await API.detectTextLanguage(segmentsPlainText.join('\n\n'), options.languageDetection || {})
|
|
67
69
|
|
|
68
70
|
options.language = detectedLanguage
|
|
69
71
|
|
|
@@ -86,7 +88,7 @@ async function synthesizeSegments(segments: string[], options: SynthesisOptions,
|
|
|
86
88
|
const { bestMatchingVoice } = await requestVoiceList(options)
|
|
87
89
|
|
|
88
90
|
if (!bestMatchingVoice) {
|
|
89
|
-
throw new Error(
|
|
91
|
+
throw new Error('No matching voice found')
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
options.voice = bestMatchingVoice.name
|
|
@@ -110,12 +112,12 @@ async function synthesizeSegments(segments: string[], options: SynthesisOptions,
|
|
|
110
112
|
for (let segmentIndex = 0; segmentIndex < segments.length; segmentIndex++) {
|
|
111
113
|
const segmentText = segments[segmentIndex].trim()
|
|
112
114
|
|
|
113
|
-
logger.log(`\n${chalk.magentaBright(`Synthesizing segment ${segmentIndex + 1}/${segments.length}`)}:
|
|
115
|
+
logger.log(`\n${chalk.magentaBright(`Synthesizing segment ${segmentIndex + 1}/${segments.length}`)}: '${segmentText}'`)
|
|
114
116
|
|
|
115
117
|
const segmentStartTime = timeOffset
|
|
116
118
|
|
|
117
119
|
const segmentEntry: TimelineEntry = {
|
|
118
|
-
type:
|
|
120
|
+
type: 'segment',
|
|
119
121
|
text: segmentText,
|
|
120
122
|
startTime: timeOffset,
|
|
121
123
|
endTime: -1,
|
|
@@ -124,12 +126,12 @@ async function synthesizeSegments(segments: string[], options: SynthesisOptions,
|
|
|
124
126
|
|
|
125
127
|
let sentences: string[]
|
|
126
128
|
|
|
127
|
-
if ((options.splitToSentences || options.engine ==
|
|
129
|
+
if ((options.splitToSentences || options.engine == 'vits') && !options.ssml) {
|
|
128
130
|
sentences = splitToSentences(segmentText, options.language!)
|
|
129
|
-
sentences = sentences.filter(sentence => sentence.trim() !=
|
|
131
|
+
sentences = sentences.filter(sentence => sentence.trim() != '')
|
|
130
132
|
|
|
131
133
|
if (sentences.length == 0) {
|
|
132
|
-
sentences = [
|
|
134
|
+
sentences = ['']
|
|
133
135
|
}
|
|
134
136
|
} else {
|
|
135
137
|
sentences = [segmentText]
|
|
@@ -142,8 +144,8 @@ async function synthesizeSegments(segments: string[], options: SynthesisOptions,
|
|
|
142
144
|
await yieldToEventLoop()
|
|
143
145
|
|
|
144
146
|
if (shouldCancelCurrentTask()) {
|
|
145
|
-
//log(
|
|
146
|
-
throw new Error(
|
|
147
|
+
//log('\n\n\n\n\nCANCELED\n\n\n\n')
|
|
148
|
+
throw new Error('Canceled')
|
|
147
149
|
}
|
|
148
150
|
|
|
149
151
|
const sentenceText = sentences[sentenceIndex].trim()
|
|
@@ -175,14 +177,14 @@ async function synthesizeSegments(segments: string[], options: SynthesisOptions,
|
|
|
175
177
|
const sentenceEndTime = timeOffset - endPause
|
|
176
178
|
|
|
177
179
|
segmentEntry.timeline!.push({
|
|
178
|
-
type:
|
|
180
|
+
type: 'sentence',
|
|
179
181
|
text: sentenceText,
|
|
180
182
|
startTime: sentenceStartTime,
|
|
181
183
|
endTime: sentenceEndTime,
|
|
182
184
|
timeline: sentenceTimelineWithOffset
|
|
183
185
|
})
|
|
184
186
|
|
|
185
|
-
peakDecibelsSoFar = Math.max(peakDecibelsSoFar,
|
|
187
|
+
peakDecibelsSoFar = Math.max(peakDecibelsSoFar, getSamplePeakDecibels(sentenceRawAudio.audioChannels))
|
|
186
188
|
|
|
187
189
|
const sentenceAudio = await convertToTargetCodecIfNeeded(sentenceRawAudio)
|
|
188
190
|
|
|
@@ -245,7 +247,9 @@ async function synthesizeSegments(segments: string[], options: SynthesisOptions,
|
|
|
245
247
|
resultRawAudio = { audioChannels: joinedAudioBuffers, sampleRate: segmentsRawAudio[0].sampleRate }
|
|
246
248
|
|
|
247
249
|
if (options.postProcessing!.normalizeAudio) {
|
|
248
|
-
resultRawAudio = normalizeAudioLevel(resultRawAudio, options.postProcessing!.
|
|
250
|
+
resultRawAudio = normalizeAudioLevel(resultRawAudio, options.postProcessing!.targetPeak, options.postProcessing!.maxGainIncrease)
|
|
251
|
+
} else {
|
|
252
|
+
resultRawAudio = attenuateIfClipping(resultRawAudio)
|
|
249
253
|
}
|
|
250
254
|
} else {
|
|
251
255
|
resultRawAudio = getEmptyRawAudio(1, 24000)
|
|
@@ -259,8 +263,8 @@ async function synthesizeSegments(segments: string[], options: SynthesisOptions,
|
|
|
259
263
|
if (targetCodec) {
|
|
260
264
|
logger.start(`Convert to ${targetCodec} codec`)
|
|
261
265
|
|
|
262
|
-
if (targetCodec ==
|
|
263
|
-
output =
|
|
266
|
+
if (targetCodec == 'wav') {
|
|
267
|
+
output = encodeRawAudioToWave(rawAudio)
|
|
264
268
|
} else {
|
|
265
269
|
const ffmpegOptions = FFMpegTranscoder.getDefaultFFMpegOptionsForSpeech(targetCodec, options.outputAudioFormat?.bitrate)
|
|
266
270
|
output = await FFMpegTranscoder.encodeFromChannels(rawAudio, ffmpegOptions)
|
|
@@ -293,9 +297,10 @@ export interface SynthesisResult {
|
|
|
293
297
|
|
|
294
298
|
async function synthesizeSegment(text: string, options: SynthesisOptions) {
|
|
295
299
|
const logger = new Logger()
|
|
300
|
+
|
|
296
301
|
const startTimestamp = logger.getTimestamp()
|
|
297
302
|
|
|
298
|
-
logger.start(
|
|
303
|
+
logger.start('Prepare for synthesis')
|
|
299
304
|
|
|
300
305
|
const simplifiedText = simplifyPunctuationCharacters(text)
|
|
301
306
|
|
|
@@ -306,7 +311,7 @@ async function synthesizeSegment(text: string, options: SynthesisOptions) {
|
|
|
306
311
|
const { bestMatchingVoice } = await requestVoiceList(options)
|
|
307
312
|
|
|
308
313
|
if (!bestMatchingVoice) {
|
|
309
|
-
throw new Error(
|
|
314
|
+
throw new Error('No matching voice found')
|
|
310
315
|
}
|
|
311
316
|
|
|
312
317
|
const selectedVoice = bestMatchingVoice
|
|
@@ -338,18 +343,18 @@ async function synthesizeSegment(text: string, options: SynthesisOptions) {
|
|
|
338
343
|
let shouldPostprocessPitch = false
|
|
339
344
|
|
|
340
345
|
switch (engine) {
|
|
341
|
-
case
|
|
346
|
+
case 'vits': {
|
|
342
347
|
if (inputIsSSML) {
|
|
343
348
|
throw new Error(`The VITS engine doesn't currently support SSML inputs`)
|
|
344
349
|
}
|
|
345
350
|
|
|
346
351
|
let vitsLanguage = language
|
|
347
352
|
|
|
348
|
-
if (vitsLanguage ==
|
|
349
|
-
vitsLanguage =
|
|
353
|
+
if (vitsLanguage == 'en') {
|
|
354
|
+
vitsLanguage = 'en-us'
|
|
350
355
|
}
|
|
351
356
|
|
|
352
|
-
const vitsTTS = await import(
|
|
357
|
+
const vitsTTS = await import('../synthesis/VitsTTS.js')
|
|
353
358
|
|
|
354
359
|
const lengthScale = 1 / speed
|
|
355
360
|
|
|
@@ -360,7 +365,7 @@ async function synthesizeSegment(text: string, options: SynthesisOptions) {
|
|
|
360
365
|
if (speakerId != undefined) {
|
|
361
366
|
if (selectedVoice.speakerCount == undefined) {
|
|
362
367
|
if (speakerId != 0) {
|
|
363
|
-
throw new Error(
|
|
368
|
+
throw new Error('Selected VITS model has only one speaker. Speaker ID must be 0 if specified.')
|
|
364
369
|
}
|
|
365
370
|
} else if (speakerId < 0 || speakerId >= selectedVoice.speakerCount) {
|
|
366
371
|
throw new Error(`Selected VITS model has ${selectedVoice.speakerCount} voices. Speaker ID should be in the range ${0} to ${selectedVoice.speakerCount - 1}`)
|
|
@@ -385,18 +390,18 @@ async function synthesizeSegment(text: string, options: SynthesisOptions) {
|
|
|
385
390
|
break
|
|
386
391
|
}
|
|
387
392
|
|
|
388
|
-
case
|
|
393
|
+
case 'pico': {
|
|
389
394
|
if (inputIsSSML) {
|
|
390
395
|
throw new Error(`The SVOX Pico engine doesn't currently support SSML inputs`)
|
|
391
396
|
}
|
|
392
397
|
|
|
393
|
-
const SvoxPicoTTS = await import(
|
|
398
|
+
const SvoxPicoTTS = await import('../synthesis/SvoxPicoTTS.js')
|
|
394
399
|
|
|
395
400
|
const picoSpeed = Math.round(speed * 1.0 * 100)
|
|
396
401
|
const picoPitch = Math.round(pitch * 1.0 * 100)
|
|
397
402
|
const picoVolume = 35.0
|
|
398
403
|
|
|
399
|
-
const preparedText = `<speed level=
|
|
404
|
+
const preparedText = `<speed level='${picoSpeed}'><pitch level='${picoPitch}'><volume level='${picoVolume}'>${simplifiedText}</volume></pitch></speed>`
|
|
400
405
|
|
|
401
406
|
logger.end()
|
|
402
407
|
|
|
@@ -412,12 +417,12 @@ async function synthesizeSegment(text: string, options: SynthesisOptions) {
|
|
|
412
417
|
break
|
|
413
418
|
}
|
|
414
419
|
|
|
415
|
-
case
|
|
420
|
+
case 'flite': {
|
|
416
421
|
if (inputIsSSML) {
|
|
417
422
|
throw new Error(`The Flite engine doesn't currently support SSML inputs`)
|
|
418
423
|
}
|
|
419
424
|
|
|
420
|
-
const FliteTTS = await import(
|
|
425
|
+
const FliteTTS = await import('../synthesis/FliteTTS.js')
|
|
421
426
|
|
|
422
427
|
logger.end()
|
|
423
428
|
|
|
@@ -430,8 +435,8 @@ async function synthesizeSegment(text: string, options: SynthesisOptions) {
|
|
|
430
435
|
break
|
|
431
436
|
}
|
|
432
437
|
|
|
433
|
-
case
|
|
434
|
-
const EspeakTTS = await import(
|
|
438
|
+
case 'espeak': {
|
|
439
|
+
const EspeakTTS = await import('../synthesis/EspeakTTS.js')
|
|
435
440
|
|
|
436
441
|
const engineOptions = options.espeak!
|
|
437
442
|
|
|
@@ -441,6 +446,7 @@ async function synthesizeSegment(text: string, options: SynthesisOptions) {
|
|
|
441
446
|
const espeakPitch = engineOptions.pitch || options.pitch! * 50
|
|
442
447
|
const espeakPitchRange = engineOptions.pitchRange || options.pitchVariation! * 50
|
|
443
448
|
const espeakUseKlatt = engineOptions.useKlatt || false
|
|
449
|
+
const espeakInsertSeparators = engineOptions.insertSeparators || false
|
|
444
450
|
|
|
445
451
|
const espeakOptions: EspeakOptions = {
|
|
446
452
|
voice: espeakVoice,
|
|
@@ -448,7 +454,8 @@ async function synthesizeSegment(text: string, options: SynthesisOptions) {
|
|
|
448
454
|
rate: espeakRate,
|
|
449
455
|
pitch: espeakPitch,
|
|
450
456
|
pitchRange: espeakPitchRange,
|
|
451
|
-
useKlatt: espeakUseKlatt
|
|
457
|
+
useKlatt: espeakUseKlatt,
|
|
458
|
+
insertSeparators: espeakInsertSeparators,
|
|
452
459
|
}
|
|
453
460
|
|
|
454
461
|
if (inputIsSSML) {
|
|
@@ -471,12 +478,12 @@ async function synthesizeSegment(text: string, options: SynthesisOptions) {
|
|
|
471
478
|
break
|
|
472
479
|
}
|
|
473
480
|
|
|
474
|
-
case
|
|
481
|
+
case 'sam': {
|
|
475
482
|
if (inputIsSSML) {
|
|
476
483
|
throw new Error(`The SAM engine doesn't support SSML inputs`)
|
|
477
484
|
}
|
|
478
485
|
|
|
479
|
-
const SamTTS = await import(
|
|
486
|
+
const SamTTS = await import('../synthesis/SamTTS.js')
|
|
480
487
|
|
|
481
488
|
const engineOptions = options.sam!
|
|
482
489
|
|
|
@@ -494,12 +501,12 @@ async function synthesizeSegment(text: string, options: SynthesisOptions) {
|
|
|
494
501
|
break
|
|
495
502
|
}
|
|
496
503
|
|
|
497
|
-
case
|
|
504
|
+
case 'sapi': {
|
|
498
505
|
if (inputIsSSML) {
|
|
499
506
|
throw new Error(`The SAPI engine doesn't currently support SSML inputs`)
|
|
500
507
|
}
|
|
501
508
|
|
|
502
|
-
const SapiTTS = await import(
|
|
509
|
+
const SapiTTS = await import('../synthesis/SapiTTS.js')
|
|
503
510
|
|
|
504
511
|
await SapiTTS.AssertSAPIAvailable(false)
|
|
505
512
|
|
|
@@ -520,12 +527,12 @@ async function synthesizeSegment(text: string, options: SynthesisOptions) {
|
|
|
520
527
|
break
|
|
521
528
|
}
|
|
522
529
|
|
|
523
|
-
case
|
|
530
|
+
case 'msspeech': {
|
|
524
531
|
if (inputIsSSML) {
|
|
525
532
|
throw new Error(`The MSSpeech engine doesn't currently support SSML inputs`)
|
|
526
533
|
}
|
|
527
534
|
|
|
528
|
-
const SapiTTS = await import(
|
|
535
|
+
const SapiTTS = await import('../synthesis/SapiTTS.js')
|
|
529
536
|
|
|
530
537
|
await SapiTTS.AssertSAPIAvailable(true)
|
|
531
538
|
|
|
@@ -546,12 +553,12 @@ async function synthesizeSegment(text: string, options: SynthesisOptions) {
|
|
|
546
553
|
break
|
|
547
554
|
}
|
|
548
555
|
|
|
549
|
-
case
|
|
556
|
+
case 'coqui-server': {
|
|
550
557
|
if (inputIsSSML) {
|
|
551
558
|
throw new Error(`The Coqui Server engine doesn't support SSML inputs`)
|
|
552
559
|
}
|
|
553
560
|
|
|
554
|
-
const CoquiServerTTS = await import(
|
|
561
|
+
const CoquiServerTTS = await import('../synthesis/CoquiServerTTS.js')
|
|
555
562
|
|
|
556
563
|
const engineOptions = options.coquiServer!
|
|
557
564
|
|
|
@@ -574,15 +581,15 @@ async function synthesizeSegment(text: string, options: SynthesisOptions) {
|
|
|
574
581
|
break
|
|
575
582
|
}
|
|
576
583
|
|
|
577
|
-
case
|
|
578
|
-
const GoogleCloudTTS = await import(
|
|
584
|
+
case 'google-cloud': {
|
|
585
|
+
const GoogleCloudTTS = await import('../synthesis/GoogleCloudTTS.js')
|
|
579
586
|
|
|
580
587
|
const engineOptions = options.googleCloud!
|
|
581
588
|
|
|
582
589
|
const apiKey = engineOptions.apiKey
|
|
583
590
|
|
|
584
591
|
if (!apiKey) {
|
|
585
|
-
throw new Error(`No API key
|
|
592
|
+
throw new Error(`No Google Cloud API key provided`)
|
|
586
593
|
}
|
|
587
594
|
|
|
588
595
|
let pitchDeltaSemitones: number
|
|
@@ -607,21 +614,21 @@ async function synthesizeSegment(text: string, options: SynthesisOptions) {
|
|
|
607
614
|
break
|
|
608
615
|
}
|
|
609
616
|
|
|
610
|
-
case
|
|
611
|
-
const AzureCognitiveServicesTTS = await import(
|
|
617
|
+
case 'microsoft-azure': {
|
|
618
|
+
const AzureCognitiveServicesTTS = await import('../synthesis/AzureCognitiveServicesTTS.js')
|
|
612
619
|
|
|
613
620
|
const engineOptions = options.microsoftAzure!
|
|
614
621
|
|
|
615
622
|
const subscriptionKey = engineOptions.subscriptionKey
|
|
616
623
|
|
|
617
624
|
if (!subscriptionKey) {
|
|
618
|
-
throw new Error(`No subscription key
|
|
625
|
+
throw new Error(`No Microsoft Azure subscription key provided`)
|
|
619
626
|
}
|
|
620
627
|
|
|
621
628
|
const serviceRegion = engineOptions!.serviceRegion
|
|
622
629
|
|
|
623
630
|
if (!serviceRegion) {
|
|
624
|
-
throw new Error(`No service region
|
|
631
|
+
throw new Error(`No Microsoft Azure service region provided`)
|
|
625
632
|
}
|
|
626
633
|
|
|
627
634
|
let ssmlPitch: string
|
|
@@ -648,27 +655,27 @@ async function synthesizeSegment(text: string, options: SynthesisOptions) {
|
|
|
648
655
|
break
|
|
649
656
|
}
|
|
650
657
|
|
|
651
|
-
case
|
|
652
|
-
const AwsPollyTTS = await import(
|
|
658
|
+
case 'amazon-polly': {
|
|
659
|
+
const AwsPollyTTS = await import('../synthesis/AwsPollyTTS.js')
|
|
653
660
|
|
|
654
661
|
const engineOptions = options.amazonPolly!
|
|
655
662
|
|
|
656
663
|
const region = engineOptions.region
|
|
657
664
|
|
|
658
665
|
if (!region) {
|
|
659
|
-
throw new Error(`No region
|
|
666
|
+
throw new Error(`No Amazon Polly region provided`)
|
|
660
667
|
}
|
|
661
668
|
|
|
662
669
|
const accessKeyId = engineOptions.accessKeyId
|
|
663
670
|
|
|
664
671
|
if (!accessKeyId) {
|
|
665
|
-
throw new Error(`No access key id
|
|
672
|
+
throw new Error(`No Amazon Polly access key id provided`)
|
|
666
673
|
}
|
|
667
674
|
|
|
668
675
|
const secretAccessKey = engineOptions.secretAccessKey
|
|
669
676
|
|
|
670
677
|
if (!secretAccessKey) {
|
|
671
|
-
throw new Error(`No secret access key
|
|
678
|
+
throw new Error(`No Amazon Polly secret access key provided`)
|
|
672
679
|
}
|
|
673
680
|
|
|
674
681
|
const pollyEngine = engineOptions.pollyEngine
|
|
@@ -686,29 +693,44 @@ async function synthesizeSegment(text: string, options: SynthesisOptions) {
|
|
|
686
693
|
break
|
|
687
694
|
}
|
|
688
695
|
|
|
689
|
-
case
|
|
696
|
+
case 'openai-cloud': {
|
|
697
|
+
const OpenAICloudTTS = await import('../synthesis/OpenAICloudTTS.js')
|
|
698
|
+
|
|
699
|
+
const openAICloudTTSOptions = options.openAICloud!
|
|
700
|
+
|
|
701
|
+
if (!openAICloudTTSOptions.apiKey) {
|
|
702
|
+
throw new Error(`No API key given`)
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
logger.end();
|
|
706
|
+
|
|
707
|
+
synthesizedAudio = await OpenAICloudTTS.synthesize(text, voice, speed, openAICloudTTSOptions)
|
|
708
|
+
|
|
709
|
+
shouldPostprocessSpeed = false
|
|
710
|
+
shouldPostprocessPitch = true
|
|
711
|
+
|
|
712
|
+
break
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
case 'elevenlabs': {
|
|
690
716
|
if (inputIsSSML) {
|
|
691
717
|
throw new Error(`The Elevenlabs engine doesn't support SSML inputs`)
|
|
692
718
|
}
|
|
693
719
|
|
|
694
|
-
const ElevenLabsTTS = await import(
|
|
720
|
+
const ElevenLabsTTS = await import('../synthesis/ElevenlabsTTS.js')
|
|
695
721
|
|
|
696
722
|
const engineOptions = options.elevenlabs!
|
|
697
723
|
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
if (!apiKey) {
|
|
701
|
-
throw new Error(`No ElevenLabs API key given`)
|
|
724
|
+
if (!engineOptions.apiKey) {
|
|
725
|
+
throw new Error(`No ElevenLabs API key provided`)
|
|
702
726
|
}
|
|
703
727
|
|
|
704
|
-
const voiceId = (selectedVoice as any)[
|
|
705
|
-
const modelId = (selectedVoice as any)[
|
|
706
|
-
const stability = engineOptions.stability!
|
|
707
|
-
const similarityBoost = engineOptions.similarityBoost!
|
|
728
|
+
const voiceId = (selectedVoice as any)['elevenLabsVoiceId']
|
|
729
|
+
const modelId = (selectedVoice as any)['elevenLabsModelId']
|
|
708
730
|
|
|
709
731
|
logger.end()
|
|
710
732
|
|
|
711
|
-
const { rawAudio } = await ElevenLabsTTS.synthesize(text, voiceId,
|
|
733
|
+
const { rawAudio } = await ElevenLabsTTS.synthesize(text, voiceId, modelId, engineOptions)
|
|
712
734
|
|
|
713
735
|
synthesizedAudio = rawAudio
|
|
714
736
|
|
|
@@ -718,12 +740,12 @@ async function synthesizeSegment(text: string, options: SynthesisOptions) {
|
|
|
718
740
|
break
|
|
719
741
|
}
|
|
720
742
|
|
|
721
|
-
case
|
|
743
|
+
case 'google-translate': {
|
|
722
744
|
if (inputIsSSML) {
|
|
723
745
|
throw new Error(`The Google Translate engine doesn't support SSML inputs`)
|
|
724
746
|
}
|
|
725
747
|
|
|
726
|
-
const GoogleTranslateTTS = await import(
|
|
748
|
+
const GoogleTranslateTTS = await import('../synthesis/GoogleTranslateTTS.js')
|
|
727
749
|
|
|
728
750
|
logger.end()
|
|
729
751
|
|
|
@@ -745,23 +767,23 @@ async function synthesizeSegment(text: string, options: SynthesisOptions) {
|
|
|
745
767
|
break
|
|
746
768
|
}
|
|
747
769
|
|
|
748
|
-
case
|
|
770
|
+
case 'microsoft-edge': {
|
|
749
771
|
if (inputIsSSML) {
|
|
750
772
|
throw new Error(`The Microsoft Edge engine doesn't support SSML inputs`)
|
|
751
773
|
}
|
|
752
774
|
|
|
753
|
-
const MicrosoftEdgeTTS = await import(
|
|
775
|
+
const MicrosoftEdgeTTS = await import('../synthesis/MicrosoftEdgeTTS.js')
|
|
754
776
|
|
|
755
777
|
const engineOptions = options.microsoftEdge!
|
|
756
778
|
|
|
757
779
|
const trustedClientToken = engineOptions.trustedClientToken
|
|
758
780
|
|
|
759
781
|
if (!trustedClientToken) {
|
|
760
|
-
throw new Error(
|
|
782
|
+
throw new Error('No Microsoft Edge trusted client token provided')
|
|
761
783
|
}
|
|
762
784
|
|
|
763
|
-
if (await sha256AsHex(trustedClientToken) !=
|
|
764
|
-
throw new Error(
|
|
785
|
+
if (await sha256AsHex(trustedClientToken) != '558d7c6a7f7db444895946fe23a54ad172fd6d159f46cb34dd4db21bb27c07d7') {
|
|
786
|
+
throw new Error('Trusted client token is incorrect.')
|
|
765
787
|
}
|
|
766
788
|
|
|
767
789
|
let ssmlPitch: string
|
|
@@ -791,12 +813,12 @@ async function synthesizeSegment(text: string, options: SynthesisOptions) {
|
|
|
791
813
|
break
|
|
792
814
|
}
|
|
793
815
|
|
|
794
|
-
case
|
|
816
|
+
case 'streamlabs-polly': {
|
|
795
817
|
if (inputIsSSML) {
|
|
796
818
|
throw new Error(`The Streamlabs Polly Engine engine doesn't support SSML inputs`)
|
|
797
819
|
}
|
|
798
820
|
|
|
799
|
-
const StreamlabsPollyTTS = await import(
|
|
821
|
+
const StreamlabsPollyTTS = await import('../synthesis/StreamlabsPollyTTS.js')
|
|
800
822
|
|
|
801
823
|
logger.end()
|
|
802
824
|
|
|
@@ -820,11 +842,13 @@ async function synthesizeSegment(text: string, options: SynthesisOptions) {
|
|
|
820
842
|
}
|
|
821
843
|
}
|
|
822
844
|
|
|
823
|
-
logger.start(
|
|
845
|
+
logger.start('Postprocess synthesized audio')
|
|
824
846
|
synthesizedAudio = downmixToMono(synthesizedAudio)
|
|
825
847
|
|
|
826
848
|
if (options.postProcessing!.normalizeAudio) {
|
|
827
|
-
synthesizedAudio = normalizeAudioLevel(synthesizedAudio, options.postProcessing!.
|
|
849
|
+
synthesizedAudio = normalizeAudioLevel(synthesizedAudio, options.postProcessing!.targetPeak!, options.postProcessing!.maxGainIncrease!)
|
|
850
|
+
} else {
|
|
851
|
+
synthesizedAudio = attenuateIfClipping(synthesizedAudio)
|
|
828
852
|
}
|
|
829
853
|
|
|
830
854
|
const preTrimSampleCount = synthesizedAudio.audioChannels[0].length
|
|
@@ -838,7 +862,7 @@ async function synthesizeSegment(text: string, options: SynthesisOptions) {
|
|
|
838
862
|
}
|
|
839
863
|
|
|
840
864
|
if (!timeline) {
|
|
841
|
-
logger.start(
|
|
865
|
+
logger.start('Align synthesized audio with text')
|
|
842
866
|
|
|
843
867
|
let plainText = text
|
|
844
868
|
|
|
@@ -880,17 +904,17 @@ async function synthesizeSegment(text: string, options: SynthesisOptions) {
|
|
|
880
904
|
}
|
|
881
905
|
|
|
882
906
|
if ((timeStretchFactor != undefined && timeStretchFactor != 1.0) || (pitchShiftFactor != undefined && pitchShiftFactor != 1.0)) {
|
|
883
|
-
logger.start(
|
|
907
|
+
logger.start('Apply time and pitch shifting')
|
|
884
908
|
|
|
885
909
|
timeStretchFactor = timeStretchFactor || 1.0
|
|
886
910
|
pitchShiftFactor = pitchShiftFactor || 1.0
|
|
887
911
|
|
|
888
912
|
const timePitchShiftingMethod = postProcessingOptions.timePitchShiftingMethod
|
|
889
913
|
|
|
890
|
-
if (timePitchShiftingMethod ==
|
|
914
|
+
if (timePitchShiftingMethod == 'sonic') {
|
|
891
915
|
const sonic = await import('../dsp/Sonic.js')
|
|
892
916
|
synthesizedAudio = await sonic.stretchTimePitch(synthesizedAudio, timeStretchFactor, pitchShiftFactor)
|
|
893
|
-
} else if (timePitchShiftingMethod ==
|
|
917
|
+
} else if (timePitchShiftingMethod == 'rubberband') {
|
|
894
918
|
const rubberband = await import('../dsp/Rubberband.js')
|
|
895
919
|
|
|
896
920
|
const rubberbandOptions: RubberbandOptions = extendDeep(rubberband.defaultRubberbandOptions, postProcessingOptions.rubberband || {})
|
|
@@ -928,10 +952,10 @@ function convertSpeedScaleToSSMLValueString(rate: number) {
|
|
|
928
952
|
|
|
929
953
|
function convertPitchScaleToSSMLValueString(pitch: number, voiceGender: VoiceGender) {
|
|
930
954
|
let fundementalFrequency
|
|
931
|
-
if (voiceGender ==
|
|
955
|
+
if (voiceGender == 'male') {
|
|
932
956
|
// Use an estimate of the average male voice fundemental frequency
|
|
933
957
|
fundementalFrequency = 120
|
|
934
|
-
} else if (voiceGender ==
|
|
958
|
+
} else if (voiceGender == 'female') {
|
|
935
959
|
// Use an estimate of the average female voice fundemental frequency
|
|
936
960
|
fundementalFrequency = 210
|
|
937
961
|
} else {
|
|
@@ -949,9 +973,9 @@ function convertPitchScaleToSSMLValueString(pitch: number, voiceGender: VoiceGen
|
|
|
949
973
|
}
|
|
950
974
|
}
|
|
951
975
|
|
|
952
|
-
export type SynthesisEngine =
|
|
976
|
+
export type SynthesisEngine = 'vits' | 'pico' | 'flite' | 'espeak' | 'sam' | 'sapi' | 'msspeech' | 'coqui-server' | 'google-cloud' | 'microsoft-azure' | 'amazon-polly' | 'openai-cloud' | 'elevenlabs' | 'google-translate' | 'microsoft-edge' | 'streamlabs-polly'
|
|
953
977
|
|
|
954
|
-
export type TimePitchShiftingMethod =
|
|
978
|
+
export type TimePitchShiftingMethod = 'sonic' | 'rubberband'
|
|
955
979
|
|
|
956
980
|
export interface SynthesisOptions {
|
|
957
981
|
engine?: SynthesisEngine
|
|
@@ -979,8 +1003,8 @@ export interface SynthesisOptions {
|
|
|
979
1003
|
|
|
980
1004
|
postProcessing?: {
|
|
981
1005
|
normalizeAudio?: boolean
|
|
982
|
-
|
|
983
|
-
|
|
1006
|
+
targetPeak?: number
|
|
1007
|
+
maxGainIncrease?: number
|
|
984
1008
|
|
|
985
1009
|
speed?: number
|
|
986
1010
|
pitch?: number
|
|
@@ -990,7 +1014,7 @@ export interface SynthesisOptions {
|
|
|
990
1014
|
}
|
|
991
1015
|
|
|
992
1016
|
outputAudioFormat?: {
|
|
993
|
-
codec?:
|
|
1017
|
+
codec?: 'wav' | 'mp3' | 'opus' | 'm4a' | 'ogg' | 'flac'
|
|
994
1018
|
bitrate?: number
|
|
995
1019
|
}
|
|
996
1020
|
|
|
@@ -1012,7 +1036,9 @@ export interface SynthesisOptions {
|
|
|
1012
1036
|
rate?: number
|
|
1013
1037
|
pitch?: number
|
|
1014
1038
|
pitchRange?: number
|
|
1039
|
+
|
|
1015
1040
|
useKlatt?: boolean
|
|
1041
|
+
insertSeparators?: boolean
|
|
1016
1042
|
}
|
|
1017
1043
|
|
|
1018
1044
|
sam?: {
|
|
@@ -1055,15 +1081,13 @@ export interface SynthesisOptions {
|
|
|
1055
1081
|
region?: string
|
|
1056
1082
|
accessKeyId?: string
|
|
1057
1083
|
secretAccessKey?: string
|
|
1058
|
-
pollyEngine?:
|
|
1084
|
+
pollyEngine?: 'standard' | 'neural'
|
|
1059
1085
|
lexiconNames?: string[]
|
|
1060
1086
|
}
|
|
1061
1087
|
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
similarityBoost?: number
|
|
1066
|
-
},
|
|
1088
|
+
openAICloud?: OpenAICloudTTSOptions
|
|
1089
|
+
|
|
1090
|
+
elevenlabs?: ElevenlabsTTSOptions,
|
|
1067
1091
|
|
|
1068
1092
|
googleTranslate?: {
|
|
1069
1093
|
tld?: string
|
|
@@ -1105,7 +1129,7 @@ export const defaultSynthesisOptions: SynthesisOptions = {
|
|
|
1105
1129
|
},
|
|
1106
1130
|
|
|
1107
1131
|
alignment: {
|
|
1108
|
-
engine:
|
|
1132
|
+
engine: 'dtw',
|
|
1109
1133
|
|
|
1110
1134
|
dtw: {
|
|
1111
1135
|
granularity: 'high'
|
|
@@ -1114,13 +1138,13 @@ export const defaultSynthesisOptions: SynthesisOptions = {
|
|
|
1114
1138
|
|
|
1115
1139
|
postProcessing: {
|
|
1116
1140
|
normalizeAudio: true,
|
|
1117
|
-
|
|
1118
|
-
|
|
1141
|
+
targetPeak: -3,
|
|
1142
|
+
maxGainIncrease: 30,
|
|
1119
1143
|
|
|
1120
1144
|
speed: undefined,
|
|
1121
1145
|
pitch: undefined,
|
|
1122
1146
|
|
|
1123
|
-
timePitchShiftingMethod:
|
|
1147
|
+
timePitchShiftingMethod: 'sonic',
|
|
1124
1148
|
rubberband: {
|
|
1125
1149
|
}
|
|
1126
1150
|
},
|
|
@@ -1164,7 +1188,7 @@ export const defaultSynthesisOptions: SynthesisOptions = {
|
|
|
1164
1188
|
},
|
|
1165
1189
|
|
|
1166
1190
|
coquiServer: {
|
|
1167
|
-
serverUrl:
|
|
1191
|
+
serverUrl: 'http://[::1]:5002',
|
|
1168
1192
|
speakerId: null
|
|
1169
1193
|
},
|
|
1170
1194
|
|
|
@@ -1192,14 +1216,12 @@ export const defaultSynthesisOptions: SynthesisOptions = {
|
|
|
1192
1216
|
lexiconNames: undefined,
|
|
1193
1217
|
},
|
|
1194
1218
|
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
similarityBoost: 0.5,
|
|
1199
|
-
},
|
|
1219
|
+
openAICloud: defaultOpenAICloudTTSOptions,
|
|
1220
|
+
|
|
1221
|
+
elevenlabs: defaultElevenlabsTTSOptions,
|
|
1200
1222
|
|
|
1201
1223
|
googleTranslate: {
|
|
1202
|
-
tld:
|
|
1224
|
+
tld: 'us'
|
|
1203
1225
|
},
|
|
1204
1226
|
|
|
1205
1227
|
microsoftEdge: {
|
|
@@ -1236,8 +1258,8 @@ export async function requestVoiceList(options: VoiceListRequestOptions): Promis
|
|
|
1236
1258
|
let voiceList: SynthesisVoice[] = []
|
|
1237
1259
|
|
|
1238
1260
|
switch (options.engine) {
|
|
1239
|
-
case
|
|
1240
|
-
const EspeakTTS = await import(
|
|
1261
|
+
case 'espeak': {
|
|
1262
|
+
const EspeakTTS = await import('../synthesis/EspeakTTS.js')
|
|
1241
1263
|
|
|
1242
1264
|
const voices = await EspeakTTS.listVoices()
|
|
1243
1265
|
|
|
@@ -1256,41 +1278,41 @@ export async function requestVoiceList(options: VoiceListRequestOptions): Promis
|
|
|
1256
1278
|
return {
|
|
1257
1279
|
name: voice.identifier,
|
|
1258
1280
|
languages,
|
|
1259
|
-
gender:
|
|
1281
|
+
gender: 'male'
|
|
1260
1282
|
}
|
|
1261
1283
|
})
|
|
1262
1284
|
|
|
1263
1285
|
break
|
|
1264
1286
|
}
|
|
1265
1287
|
|
|
1266
|
-
case
|
|
1267
|
-
const FliteTTS = await import(
|
|
1288
|
+
case 'flite': {
|
|
1289
|
+
const FliteTTS = await import('../synthesis/FliteTTS.js')
|
|
1268
1290
|
|
|
1269
1291
|
voiceList = deepClone(FliteTTS.voiceList)
|
|
1270
1292
|
|
|
1271
1293
|
break
|
|
1272
1294
|
}
|
|
1273
1295
|
|
|
1274
|
-
case
|
|
1275
|
-
const SvoxPicoTTS = await import(
|
|
1296
|
+
case 'pico': {
|
|
1297
|
+
const SvoxPicoTTS = await import('../synthesis/SvoxPicoTTS.js')
|
|
1276
1298
|
|
|
1277
1299
|
voiceList = SvoxPicoTTS.voiceList
|
|
1278
1300
|
|
|
1279
1301
|
break
|
|
1280
1302
|
}
|
|
1281
1303
|
|
|
1282
|
-
case
|
|
1304
|
+
case 'sam': {
|
|
1283
1305
|
voiceList.push({
|
|
1284
|
-
name:
|
|
1285
|
-
languages: [
|
|
1286
|
-
gender:
|
|
1306
|
+
name: 'sam',
|
|
1307
|
+
languages: ['en-US', 'en'],
|
|
1308
|
+
gender: 'male'
|
|
1287
1309
|
})
|
|
1288
1310
|
|
|
1289
1311
|
break
|
|
1290
1312
|
}
|
|
1291
1313
|
|
|
1292
|
-
case
|
|
1293
|
-
const VitsTTS = await import(
|
|
1314
|
+
case 'vits': {
|
|
1315
|
+
const VitsTTS = await import('../synthesis/VitsTTS.js')
|
|
1294
1316
|
|
|
1295
1317
|
voiceList = VitsTTS.voiceList.map(entry => {
|
|
1296
1318
|
return { ...entry, packageName: `vits-${entry.name}` }
|
|
@@ -1299,8 +1321,8 @@ export async function requestVoiceList(options: VoiceListRequestOptions): Promis
|
|
|
1299
1321
|
break
|
|
1300
1322
|
}
|
|
1301
1323
|
|
|
1302
|
-
case
|
|
1303
|
-
const SapiTTS = await import(
|
|
1324
|
+
case 'sapi': {
|
|
1325
|
+
const SapiTTS = await import('../synthesis/SapiTTS.js')
|
|
1304
1326
|
|
|
1305
1327
|
await SapiTTS.AssertSAPIAvailable(false)
|
|
1306
1328
|
|
|
@@ -1309,8 +1331,8 @@ export async function requestVoiceList(options: VoiceListRequestOptions): Promis
|
|
|
1309
1331
|
break
|
|
1310
1332
|
}
|
|
1311
1333
|
|
|
1312
|
-
case
|
|
1313
|
-
const SapiTTS = await import(
|
|
1334
|
+
case 'msspeech': {
|
|
1335
|
+
const SapiTTS = await import('../synthesis/SapiTTS.js')
|
|
1314
1336
|
|
|
1315
1337
|
await SapiTTS.AssertSAPIAvailable(true)
|
|
1316
1338
|
|
|
@@ -1319,23 +1341,23 @@ export async function requestVoiceList(options: VoiceListRequestOptions): Promis
|
|
|
1319
1341
|
break
|
|
1320
1342
|
}
|
|
1321
1343
|
|
|
1322
|
-
case
|
|
1344
|
+
case 'coqui-server': {
|
|
1323
1345
|
voiceList = [{
|
|
1324
|
-
name:
|
|
1325
|
-
languages: [
|
|
1326
|
-
gender:
|
|
1346
|
+
name: 'coqui',
|
|
1347
|
+
languages: ['en-US'],
|
|
1348
|
+
gender: 'unknown'
|
|
1327
1349
|
}]
|
|
1328
1350
|
|
|
1329
1351
|
break
|
|
1330
1352
|
}
|
|
1331
1353
|
|
|
1332
|
-
case
|
|
1333
|
-
const GoogleCloudTTS = await import(
|
|
1354
|
+
case 'google-cloud': {
|
|
1355
|
+
const GoogleCloudTTS = await import('../synthesis/GoogleCloudTTS.js')
|
|
1334
1356
|
|
|
1335
1357
|
const apiKey = options.googleCloud!.apiKey
|
|
1336
1358
|
|
|
1337
1359
|
if (!apiKey) {
|
|
1338
|
-
throw new Error(`No API key
|
|
1360
|
+
throw new Error(`No Google Cloud API key provided`)
|
|
1339
1361
|
}
|
|
1340
1362
|
|
|
1341
1363
|
const voices = await GoogleCloudTTS.getVoiceList(apiKey)
|
|
@@ -1343,25 +1365,25 @@ export async function requestVoiceList(options: VoiceListRequestOptions): Promis
|
|
|
1343
1365
|
voiceList = voices.map(voice => ({
|
|
1344
1366
|
name: voice.name,
|
|
1345
1367
|
languages: [normalizeLanguageCode(voice.languageCodes[0]), getShortLanguageCode(voice.languageCodes[0])],
|
|
1346
|
-
gender: voice.ssmlGender.toLowerCase() as (
|
|
1368
|
+
gender: voice.ssmlGender.toLowerCase() as ('male' | 'female'),
|
|
1347
1369
|
}))
|
|
1348
1370
|
|
|
1349
1371
|
break
|
|
1350
1372
|
}
|
|
1351
1373
|
|
|
1352
|
-
case
|
|
1353
|
-
const AzureCognitiveServicesTTS = await import(
|
|
1374
|
+
case 'microsoft-azure': {
|
|
1375
|
+
const AzureCognitiveServicesTTS = await import('../synthesis/AzureCognitiveServicesTTS.js')
|
|
1354
1376
|
|
|
1355
1377
|
const subscriptionKey = options.microsoftAzure!.subscriptionKey
|
|
1356
1378
|
|
|
1357
1379
|
if (!subscriptionKey) {
|
|
1358
|
-
throw new Error(`No subscription key
|
|
1380
|
+
throw new Error(`No Microsoft Azure subscription key provided`)
|
|
1359
1381
|
}
|
|
1360
1382
|
|
|
1361
1383
|
const serviceRegion = options.microsoftAzure!.serviceRegion
|
|
1362
1384
|
|
|
1363
1385
|
if (!serviceRegion) {
|
|
1364
|
-
throw new Error(`No service region
|
|
1386
|
+
throw new Error(`No Microsoft Azure service region provided`)
|
|
1365
1387
|
}
|
|
1366
1388
|
|
|
1367
1389
|
const voices = await AzureCognitiveServicesTTS.getVoiceList(subscriptionKey, serviceRegion)
|
|
@@ -1370,32 +1392,32 @@ export async function requestVoiceList(options: VoiceListRequestOptions): Promis
|
|
|
1370
1392
|
voiceList.push({
|
|
1371
1393
|
name: voice.name,
|
|
1372
1394
|
languages: [normalizeLanguageCode(voice.locale), getShortLanguageCode(voice.locale)],
|
|
1373
|
-
gender: voice.gender == 1 ?
|
|
1395
|
+
gender: voice.gender == 1 ? 'female' : 'male'
|
|
1374
1396
|
})
|
|
1375
1397
|
}
|
|
1376
1398
|
|
|
1377
1399
|
break
|
|
1378
1400
|
}
|
|
1379
1401
|
|
|
1380
|
-
case
|
|
1381
|
-
const AwsPollyTTS = await import(
|
|
1402
|
+
case 'amazon-polly': {
|
|
1403
|
+
const AwsPollyTTS = await import('../synthesis/AwsPollyTTS.js')
|
|
1382
1404
|
|
|
1383
1405
|
const region = options.amazonPolly!.region
|
|
1384
1406
|
|
|
1385
1407
|
if (!region) {
|
|
1386
|
-
throw new Error(`No region
|
|
1408
|
+
throw new Error(`No Amazon Polly region provided`)
|
|
1387
1409
|
}
|
|
1388
1410
|
|
|
1389
1411
|
const accessKeyId = options.amazonPolly!.accessKeyId
|
|
1390
1412
|
|
|
1391
1413
|
if (!accessKeyId) {
|
|
1392
|
-
throw new Error(`No access key id
|
|
1414
|
+
throw new Error(`No Amazon Polly access key id provided`)
|
|
1393
1415
|
}
|
|
1394
1416
|
|
|
1395
1417
|
const secretAccessKey = options.amazonPolly!.secretAccessKey
|
|
1396
1418
|
|
|
1397
1419
|
if (!secretAccessKey) {
|
|
1398
|
-
throw new Error(`No secret access key
|
|
1420
|
+
throw new Error(`No Amazon Polly secret access key provided`)
|
|
1399
1421
|
}
|
|
1400
1422
|
|
|
1401
1423
|
const voices = await AwsPollyTTS.getVoiceList(region, accessKeyId, secretAccessKey)
|
|
@@ -1416,22 +1438,30 @@ export async function requestVoiceList(options: VoiceListRequestOptions): Promis
|
|
|
1416
1438
|
voiceList.push({
|
|
1417
1439
|
name: voice.Id!,
|
|
1418
1440
|
languages: languageCodes,
|
|
1419
|
-
gender: voice.Gender!.toLowerCase() as (
|
|
1441
|
+
gender: voice.Gender!.toLowerCase() as ('male' | 'female')
|
|
1420
1442
|
})
|
|
1421
1443
|
}
|
|
1422
1444
|
|
|
1423
1445
|
break
|
|
1424
1446
|
}
|
|
1425
1447
|
|
|
1426
|
-
case
|
|
1427
|
-
const
|
|
1448
|
+
case 'openai-cloud': {
|
|
1449
|
+
const OpenAICloudTTS = await import('../synthesis/OpenAICloudTTS.js')
|
|
1450
|
+
|
|
1451
|
+
voiceList = OpenAICloudTTS.voiceList
|
|
1452
|
+
|
|
1453
|
+
break
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
case 'elevenlabs': {
|
|
1457
|
+
const ElevenLabsTTS = await import('../synthesis/ElevenlabsTTS.js')
|
|
1428
1458
|
|
|
1429
1459
|
const engineOptions = options.elevenlabs!
|
|
1430
1460
|
|
|
1431
1461
|
const apiKey = engineOptions.apiKey
|
|
1432
1462
|
|
|
1433
1463
|
if (!apiKey) {
|
|
1434
|
-
throw new Error(`No Elevenlabs API key
|
|
1464
|
+
throw new Error(`No Elevenlabs API key provided`)
|
|
1435
1465
|
}
|
|
1436
1466
|
|
|
1437
1467
|
voiceList = await ElevenLabsTTS.getVoiceList(apiKey)
|
|
@@ -1439,29 +1469,29 @@ export async function requestVoiceList(options: VoiceListRequestOptions): Promis
|
|
|
1439
1469
|
break
|
|
1440
1470
|
}
|
|
1441
1471
|
|
|
1442
|
-
case
|
|
1443
|
-
const GoogleTranslateTTS = await import(
|
|
1472
|
+
case 'google-translate': {
|
|
1473
|
+
const GoogleTranslateTTS = await import('../synthesis/GoogleTranslateTTS.js')
|
|
1444
1474
|
|
|
1445
1475
|
const langLookup = GoogleTranslateTTS.supportedLanguageLookup
|
|
1446
1476
|
|
|
1447
1477
|
for (const langCode in langLookup) {
|
|
1448
1478
|
voiceList.push({
|
|
1449
1479
|
name: langLookup[langCode],
|
|
1450
|
-
languages: langCode.includes(
|
|
1451
|
-
gender:
|
|
1480
|
+
languages: langCode.includes('-') ? [normalizeLanguageCode(langCode), getShortLanguageCode(langCode)] : [normalizeLanguageCode(langCode)],
|
|
1481
|
+
gender: 'unknown'
|
|
1452
1482
|
})
|
|
1453
1483
|
}
|
|
1454
1484
|
|
|
1455
1485
|
break
|
|
1456
1486
|
}
|
|
1457
1487
|
|
|
1458
|
-
case
|
|
1459
|
-
const MicrosoftEdgeTTS = await import(
|
|
1488
|
+
case 'microsoft-edge': {
|
|
1489
|
+
const MicrosoftEdgeTTS = await import('../synthesis/MicrosoftEdgeTTS.js')
|
|
1460
1490
|
|
|
1461
1491
|
const trustedClientToken = options.microsoftEdge?.trustedClientToken
|
|
1462
1492
|
|
|
1463
1493
|
if (!trustedClientToken) {
|
|
1464
|
-
throw new Error(
|
|
1494
|
+
throw new Error('No Microsoft Edge trusted client token provided')
|
|
1465
1495
|
}
|
|
1466
1496
|
|
|
1467
1497
|
const voices =
|
|
@@ -1472,14 +1502,14 @@ export async function requestVoiceList(options: VoiceListRequestOptions): Promis
|
|
|
1472
1502
|
voiceList = voices.map((voice: any) => ({
|
|
1473
1503
|
name: voice.Name,
|
|
1474
1504
|
languages: [normalizeLanguageCode(voice.Locale), getShortLanguageCode(voice.Locale)],
|
|
1475
|
-
gender: voice.Gender ==
|
|
1505
|
+
gender: voice.Gender == 'Male' ? 'male' : 'female',
|
|
1476
1506
|
}))
|
|
1477
1507
|
|
|
1478
1508
|
break
|
|
1479
1509
|
}
|
|
1480
1510
|
|
|
1481
|
-
case
|
|
1482
|
-
const StreamlabsPollyTTS = await import(
|
|
1511
|
+
case 'streamlabs-polly': {
|
|
1512
|
+
const StreamlabsPollyTTS = await import('../synthesis/StreamlabsPollyTTS.js')
|
|
1483
1513
|
|
|
1484
1514
|
voiceList = StreamlabsPollyTTS.voiceList
|
|
1485
1515
|
|
|
@@ -1503,12 +1533,12 @@ export async function requestVoiceList(options: VoiceListRequestOptions): Promis
|
|
|
1503
1533
|
voiceList = await loadVoiceList()
|
|
1504
1534
|
}
|
|
1505
1535
|
|
|
1506
|
-
const languageCode = normalizeLanguageCode(options.language ||
|
|
1536
|
+
const languageCode = normalizeLanguageCode(options.language || '')
|
|
1507
1537
|
|
|
1508
1538
|
if (languageCode) {
|
|
1509
1539
|
let filteredVoiceList = voiceList.filter(voice => voice.languages.includes(languageCode))
|
|
1510
1540
|
|
|
1511
|
-
if (filteredVoiceList.length == 0 && languageCode.includes(
|
|
1541
|
+
if (filteredVoiceList.length == 0 && languageCode.includes('-')) {
|
|
1512
1542
|
const shortLanguageCode = getShortLanguageCode(languageCode)
|
|
1513
1543
|
|
|
1514
1544
|
filteredVoiceList = voiceList.filter(voice => voice.languages.includes(shortLanguageCode))
|
|
@@ -1519,7 +1549,7 @@ export async function requestVoiceList(options: VoiceListRequestOptions): Promis
|
|
|
1519
1549
|
|
|
1520
1550
|
if (options.voiceGender) {
|
|
1521
1551
|
const genderLowercase = options.voiceGender.toLowerCase()
|
|
1522
|
-
voiceList = voiceList.filter(voice => voice.gender == genderLowercase || voice.gender ==
|
|
1552
|
+
voiceList = voiceList.filter(voice => voice.gender == genderLowercase || voice.gender == 'unknown')
|
|
1523
1553
|
}
|
|
1524
1554
|
|
|
1525
1555
|
if (options.voice) {
|
|
@@ -1568,31 +1598,31 @@ export interface RequestVoiceListResult {
|
|
|
1568
1598
|
export async function selectBestOfflineEngineForLanguage(language: string): Promise<SynthesisEngine> {
|
|
1569
1599
|
language = normalizeLanguageCode(language)
|
|
1570
1600
|
|
|
1571
|
-
const VitsTTS = await import(
|
|
1601
|
+
const VitsTTS = await import('../synthesis/VitsTTS.js')
|
|
1572
1602
|
|
|
1573
1603
|
const vitsLanguages = getAllLangCodesFromVoiceList(VitsTTS.voiceList)
|
|
1574
1604
|
|
|
1575
1605
|
if (vitsLanguages.includes(language)) {
|
|
1576
|
-
return
|
|
1606
|
+
return 'vits'
|
|
1577
1607
|
}
|
|
1578
1608
|
|
|
1579
|
-
const FliteTTS = await import(
|
|
1609
|
+
const FliteTTS = await import('../synthesis/FliteTTS.js')
|
|
1580
1610
|
|
|
1581
1611
|
const fliteLanguages = getAllLangCodesFromVoiceList(FliteTTS.voiceList)
|
|
1582
1612
|
|
|
1583
1613
|
if (fliteLanguages.includes(language)) {
|
|
1584
|
-
return
|
|
1614
|
+
return 'flite'
|
|
1585
1615
|
}
|
|
1586
1616
|
|
|
1587
|
-
const SvoxPicoTTS = await import(
|
|
1617
|
+
const SvoxPicoTTS = await import('../synthesis/SvoxPicoTTS.js')
|
|
1588
1618
|
|
|
1589
1619
|
const picoLanguages = getAllLangCodesFromVoiceList(SvoxPicoTTS.voiceList)
|
|
1590
1620
|
|
|
1591
1621
|
if (picoLanguages.includes(language)) {
|
|
1592
|
-
return
|
|
1622
|
+
return 'pico'
|
|
1593
1623
|
}
|
|
1594
1624
|
|
|
1595
|
-
return
|
|
1625
|
+
return 'espeak'
|
|
1596
1626
|
}
|
|
1597
1627
|
|
|
1598
1628
|
export function getAllLangCodesFromVoiceList(voiceList: SynthesisVoice[]) {
|
|
@@ -1649,7 +1679,7 @@ export interface SynthesisVoice {
|
|
|
1649
1679
|
packageName?: string
|
|
1650
1680
|
}
|
|
1651
1681
|
|
|
1652
|
-
export type VoiceGender =
|
|
1682
|
+
export type VoiceGender = 'male' | 'female' | 'unknown'
|
|
1653
1683
|
|
|
1654
1684
|
export const synthesisEngines: EngineMetadata[] = [
|
|
1655
1685
|
{
|
|
@@ -1673,13 +1703,13 @@ export const synthesisEngines: EngineMetadata[] = [
|
|
|
1673
1703
|
{
|
|
1674
1704
|
id: 'espeak',
|
|
1675
1705
|
name: 'eSpeak NG',
|
|
1676
|
-
description:
|
|
1706
|
+
description: `A lightweight 'robot' sounding formant-based synthesizer.`,
|
|
1677
1707
|
type: 'local'
|
|
1678
1708
|
},
|
|
1679
1709
|
{
|
|
1680
1710
|
id: 'sam',
|
|
1681
1711
|
name: 'SAM (Software Automatic Mouth)',
|
|
1682
|
-
description:
|
|
1712
|
+
description: `A classic 'robot' speech synthesizer from 1982.`,
|
|
1683
1713
|
type: 'local'
|
|
1684
1714
|
},
|
|
1685
1715
|
{
|
|
@@ -1718,6 +1748,12 @@ export const synthesisEngines: EngineMetadata[] = [
|
|
|
1718
1748
|
description: 'Amazon Polly (also: AWS Polly) cloud text-to-speech.',
|
|
1719
1749
|
type: 'cloud'
|
|
1720
1750
|
},
|
|
1751
|
+
{
|
|
1752
|
+
id: 'openai-cloud',
|
|
1753
|
+
name: 'OpenAI Cloud',
|
|
1754
|
+
description: 'OpenAI cloud text-to-speech.',
|
|
1755
|
+
type: 'cloud'
|
|
1756
|
+
},
|
|
1721
1757
|
{
|
|
1722
1758
|
id: 'elevenlabs',
|
|
1723
1759
|
name: 'Elevenlabs',
|