echogarden 0.11.22 → 0.12.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/dist/api/API.d.ts +1 -0
- package/dist/api/API.js +1 -0
- package/dist/api/API.js.map +1 -1
- package/dist/api/GlobalOptions.d.ts +6 -0
- package/dist/api/GlobalOptions.js +17 -0
- package/dist/api/GlobalOptions.js.map +1 -0
- package/dist/audio/AudioUtilities.d.ts +4 -4
- package/dist/audio/AudioUtilities.js +27 -28
- package/dist/audio/AudioUtilities.js.map +1 -1
- package/dist/audio/SoxPath.js +18 -17
- package/dist/audio/SoxPath.js.map +1 -1
- package/dist/codecs/FFMpegTranscoder.js +45 -13
- package/dist/codecs/FFMpegTranscoder.js.map +1 -1
- package/dist/synthesis/VitsTTS.js +68 -0
- package/dist/synthesis/VitsTTS.js.map +1 -1
- package/dist/utilities/PackageManager.js +60 -38
- package/dist/utilities/PackageManager.js.map +1 -1
- package/docs/API.md +25 -0
- package/docs/Engines.md +1 -1
- package/package.json +6 -6
- package/src/api/API.ts +1 -0
- package/src/api/GlobalOptions.ts +25 -0
- package/src/audio/AudioUtilities.ts +29 -29
- package/src/audio/SoxPath.ts +24 -17
- package/src/codecs/FFMpegTranscoder.ts +43 -14
- package/src/synthesis/VitsTTS.ts +72 -0
- package/src/utilities/PackageManager.ts +68 -44
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import path from
|
|
2
|
-
import { downloadAndExtractTarball } from
|
|
3
|
-
import { getAppDataDir, getAppTempDir, ensureDir, existsSync, remove } from
|
|
4
|
-
import { appName } from
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { downloadAndExtractTarball } from './FileDownloader.js';
|
|
3
|
+
import { getAppDataDir, getAppTempDir, ensureDir, existsSync, remove } from './FileSystem.js';
|
|
4
|
+
import { appName } from '../api/Common.js';
|
|
5
5
|
export async function loadPackage(packageName) {
|
|
6
6
|
packageName = resolveToVersionedPackageNameIfNeeded(packageName);
|
|
7
7
|
const packagesPath = await ensureAndGetPackagesDir();
|
|
@@ -26,7 +26,7 @@ export async function removePackage(packageName) {
|
|
|
26
26
|
}
|
|
27
27
|
export async function ensureAndGetPackagesDir() {
|
|
28
28
|
const dataPath = getAppDataDir(appName);
|
|
29
|
-
const packagesPath = path.join(dataPath,
|
|
29
|
+
const packagesPath = path.join(dataPath, 'packages');
|
|
30
30
|
await ensureDir(packagesPath);
|
|
31
31
|
return packagesPath;
|
|
32
32
|
}
|
|
@@ -44,39 +44,61 @@ export function getVersionTagFromPackageName(packageName) {
|
|
|
44
44
|
export function resolveVersionTagForUnversionedPackageName(unversionedPackageName) {
|
|
45
45
|
return packageVersionTagResolutionLookup[unversionedPackageName] || defaultVersionTag;
|
|
46
46
|
}
|
|
47
|
-
const basePackageUrl =
|
|
48
|
-
const defaultVersionTag =
|
|
47
|
+
const basePackageUrl = 'https://huggingface.co/echogarden/echogarden-packages/resolve/main/';
|
|
48
|
+
const defaultVersionTag = '20230718';
|
|
49
49
|
const packageVersionTagResolutionLookup = {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
50
|
+
'sox-14.4.2-linux-minimal': '20230802',
|
|
51
|
+
'vits-de_DE-thorsten_emotional-medium': '20230808',
|
|
52
|
+
'vits-en_GB-semaine-medium': '20230808',
|
|
53
|
+
'vits-fr_FR-upmc-medium': '20230808',
|
|
54
|
+
'vits-lb_LU-marylux-medium': '20230808',
|
|
55
|
+
'vits-ro_RO-mihai-medium': '20230808',
|
|
56
|
+
'vits-sr_RS-serbski_institut-medium': '20230808',
|
|
57
|
+
'vits-tr_TR-dfki-medium': '20230808',
|
|
58
|
+
'vits-cs_CZ-jirka-medium': '20230824',
|
|
59
|
+
'vits-de_DE-thorsten-high': '20230824',
|
|
60
|
+
'vits-hu_HU-anna-medium': '20230824',
|
|
61
|
+
'vits-pt_PT-tugao-medium': '20230824',
|
|
62
|
+
'vits-sk_SK-lili-medium': '20230824',
|
|
63
|
+
'vits-tr_TR-fahrettin-medium': '20230824',
|
|
64
|
+
'vits-ar_JO-kareem-medium': '20231022',
|
|
65
|
+
'vits-cs_CZ-jirka-low': '20231022',
|
|
66
|
+
'vits-en_US-hfc_male-medium': '20231022',
|
|
67
|
+
'vits-en_US-libritts_r-medium': '20231022',
|
|
68
|
+
'vits-hu_HU-imre-medium': '20231022',
|
|
69
|
+
'vits-pl_PL-mc_speech-medium': '20231022',
|
|
70
|
+
'whisper-tiny': '20231126',
|
|
71
|
+
'whisper-tiny.en': '20231126',
|
|
72
|
+
'whisper-base': '20231126',
|
|
73
|
+
'whisper-base.en': '20231126',
|
|
74
|
+
'whisper-small': '20231126',
|
|
75
|
+
'whisper-small.en': '20231126',
|
|
76
|
+
'whisper-medium': '20231126',
|
|
77
|
+
'whisper-medium.en': '20231126',
|
|
78
|
+
'whisper-large-v3': '20231126',
|
|
79
|
+
'vits-ar_JO-kareem-low': '20231126',
|
|
80
|
+
'vits-en_US-hfc_female-medium': '20231126',
|
|
81
|
+
'ffmpeg-6.0-win32-x64': '20240316',
|
|
82
|
+
'ffmpeg-6.0-win32-ia32': '20240316',
|
|
83
|
+
'ffmpeg-6.0-darwin-x64': '20240316',
|
|
84
|
+
'ffmpeg-6.0-darwin-arm64': '20240316',
|
|
85
|
+
'ffmpeg-6.0-linux-x64': '20240316',
|
|
86
|
+
'ffmpeg-6.0-linux-ia32': '20240316',
|
|
87
|
+
'ffmpeg-6.0-linux-arm64': '20240316',
|
|
88
|
+
'ffmpeg-6.0-linux-arm': '20240316',
|
|
89
|
+
'ffmpeg-6.0-freebsd-x64': '20240316',
|
|
90
|
+
'vits-de_DE-mls-medium': '20240316',
|
|
91
|
+
'vits-en_GB-cori-high': '20240316',
|
|
92
|
+
'vits-en_US-kristin-medium': '20240316',
|
|
93
|
+
'vits-en_US-ljspeech-high': '20240316',
|
|
94
|
+
'vits-en_US-ljspeech-medium': '20240316',
|
|
95
|
+
'vits-es_MX-claude-high': '20240316',
|
|
96
|
+
'vits-fa_IR-amir-medium': '20240316',
|
|
97
|
+
'vits-fa_IR-gyro-medium': '20240316',
|
|
98
|
+
'vits-fr_FR-mls-medium': '20240316',
|
|
99
|
+
'vits-fr_FR-tom-medium': '20240316',
|
|
100
|
+
'vits-nl_NL-mls-medium': '20240316',
|
|
101
|
+
'vits-sl_SI-artur-medium': '20240316',
|
|
102
|
+
'vits-tr_TR-fettah-medium': '20240316',
|
|
81
103
|
};
|
|
82
104
|
//# sourceMappingURL=PackageManager.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PackageManager.js","sourceRoot":"","sources":["../../src/utilities/PackageManager.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"PackageManager.js","sourceRoot":"","sources":["../../src/utilities/PackageManager.ts"],"names":[],"mappings":"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"}
|
package/docs/API.md
CHANGED
|
@@ -248,6 +248,31 @@ Converts subtitles to a timeline.
|
|
|
248
248
|
|
|
249
249
|
Timeline object.
|
|
250
250
|
|
|
251
|
+
## Global options
|
|
252
|
+
|
|
253
|
+
### `setGlobalOption(key, value)`
|
|
254
|
+
|
|
255
|
+
Set a global option.
|
|
256
|
+
|
|
257
|
+
Supported keys:
|
|
258
|
+
|
|
259
|
+
* `'ffmpegPath'`: override FFMpeg executable path
|
|
260
|
+
* `'soxPath'`: override SoX executable path
|
|
261
|
+
|
|
262
|
+
### `getGlobalOption(key)`
|
|
263
|
+
|
|
264
|
+
Get a global option.
|
|
265
|
+
|
|
266
|
+
Supported keys:
|
|
267
|
+
|
|
268
|
+
* `'ffmpegPath'`: get FFMpeg executable path
|
|
269
|
+
* `'soxPath'`: get SoX executable path
|
|
270
|
+
|
|
271
|
+
#### Returns:
|
|
272
|
+
|
|
273
|
+
The value associated with the given key.
|
|
274
|
+
|
|
251
275
|
## TODO
|
|
252
276
|
|
|
253
277
|
Expose more methods that may be useful for developers, like phonemization, etc.
|
|
278
|
+
|
package/docs/Engines.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
## Text to speech
|
|
5
5
|
|
|
6
6
|
**Offline**:
|
|
7
|
-
* [VITS](https://github.com/jaywalnut310/vits) (`vits`): a high-quality end-to-end neural speech synthesis architecture. Available models were trained by Michael Hansen as part of his [Piper speech synthesis system](https://github.com/rhasspy/piper). Currently there are
|
|
7
|
+
* [VITS](https://github.com/jaywalnut310/vits) (`vits`): a high-quality end-to-end neural speech synthesis architecture. Available models were trained by Michael Hansen as part of his [Piper speech synthesis system](https://github.com/rhasspy/piper). Currently there are 117 voices, in a range of languages, including English (US, UK), Spanish (ES, MX), Portuguese (PT, BR), Italian, French, German, Dutch (NL, BE), Swedish, Norwegian, Danish, Finnish, Polish, Greek, Romanian, Serbian, Czech, Hungarian, Slovak, Slovenian, Turkish, Arabic, Farsi, Russian, Ukrainian, Catalan, Luxemburgish, Icelandic, Swahili, Kazakh, Georgian, Nepali, Vietnamese and Chinese. You can listen to audio samples of all voices and languages in [Piper's samples page](https://rhasspy.github.io/piper-samples/).
|
|
8
8
|
* [SVOX Pico](https://github.com/naggety/picotts) (`pico`): a legacy diphone-based synthesis engine. Supports English (US, UK), Spanish, Italian, French, and German.
|
|
9
9
|
* [Flite](https://github.com/festvox/flite) (`flite`): a legacy diphone-based synthesis engine. Supports English (US, Scottish), and several Indic languages: Hindi, Bengali, Marathi, Telugu, Tamil, Gujarati, Kannada and Punjabi.
|
|
10
10
|
* [eSpeak-NG](https://github.com/espeak-ng/espeak-ng/) (`espeak`): a lightweight "robot" sounding formant-based synthesizer. Supports 100+ languages. Extensively used internally for speech alignment, phonemization, and other internal tasks.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "echogarden",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
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",
|
|
@@ -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.535.0",
|
|
58
|
+
"@aws-sdk/client-transcribe-streaming": "^3.535.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",
|
|
@@ -121,12 +121,12 @@
|
|
|
121
121
|
"@types/msgpack-lite": "^0.1.11",
|
|
122
122
|
"@types/ndarray": "^1.0.14",
|
|
123
123
|
"@types/ndarray-ops": "^1.2.7",
|
|
124
|
-
"@types/node": "^20.11.
|
|
124
|
+
"@types/node": "^20.11.28",
|
|
125
125
|
"@types/recursive-readdir": "^2.2.4",
|
|
126
126
|
"@types/tar": "^6.1.11",
|
|
127
127
|
"@types/ws": "^8.5.10",
|
|
128
|
-
"@typescript-eslint/eslint-plugin": "^7.
|
|
129
|
-
"@typescript-eslint/parser": "^7.
|
|
128
|
+
"@typescript-eslint/eslint-plugin": "^7.2.0",
|
|
129
|
+
"@typescript-eslint/parser": "^7.2.0",
|
|
130
130
|
"eslint": "^8.57.0",
|
|
131
131
|
"ts-json-schema-generator": "^1.5.0",
|
|
132
132
|
"typescript": "^5.4.2"
|
package/src/api/API.ts
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function getGlobalOption<K extends keyof GlobalOptions>(key: K): GlobalOptions[K] {
|
|
2
|
+
if (!Object.keys(globalOptions).includes(key)) {
|
|
3
|
+
throw new Error(`Unknown global option key '${key}'`)
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
return globalOptions[key]
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function setGlobalOption<K extends keyof GlobalOptions>(key: K, value: GlobalOptions[K]) {
|
|
10
|
+
if (!Object.keys(globalOptions).includes(key)) {
|
|
11
|
+
throw new Error(`Unknown global option key '${key}'`)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
globalOptions[key] = value as any
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface GlobalOptions {
|
|
18
|
+
ffmpegPath?: string
|
|
19
|
+
soxPath?: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const globalOptions = {
|
|
23
|
+
ffmpegPath: undefined,
|
|
24
|
+
soxPath: undefined
|
|
25
|
+
}
|
|
@@ -42,12 +42,12 @@ export function trimAudioEnd(audioSamples: Float32Array, targetEndSilentSampleCo
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
export function getStartingSilentSampleCount(audioSamples: Float32Array, amplitudeThresholdDecibels = defaultSilenceThresholdDecibels) {
|
|
45
|
-
const
|
|
45
|
+
const minSampleAmplitude = decibelsToGainFactor(amplitudeThresholdDecibels)
|
|
46
46
|
|
|
47
47
|
let silentSampleCount = 0
|
|
48
48
|
|
|
49
49
|
for (let i = 0; i < audioSamples.length - 1; i++) {
|
|
50
|
-
if (Math.abs(audioSamples[i]) >
|
|
50
|
+
if (Math.abs(audioSamples[i]) > minSampleAmplitude) {
|
|
51
51
|
break
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -58,12 +58,12 @@ export function getStartingSilentSampleCount(audioSamples: Float32Array, amplitu
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
export function getEndingSilentSampleCount(audioSamples: Float32Array, amplitudeThresholdDecibels = defaultSilenceThresholdDecibels) {
|
|
61
|
-
const
|
|
61
|
+
const minSampleAmplitude = decibelsToGainFactor(amplitudeThresholdDecibels)
|
|
62
62
|
|
|
63
63
|
let silentSampleCount = 0
|
|
64
64
|
|
|
65
65
|
for (let i = audioSamples.length - 1; i >= 0; i--) {
|
|
66
|
-
if (Math.abs(audioSamples[i]) >
|
|
66
|
+
if (Math.abs(audioSamples[i]) > minSampleAmplitude) {
|
|
67
67
|
break
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -83,14 +83,14 @@ export function downmixToMonoAndNormalize(rawAudio: RawAudio, targetPeakDb = -3)
|
|
|
83
83
|
export function normalizeAudioLevel(rawAudio: RawAudio, targetPeakDb = -3, maxIncreaseDb = 30): RawAudio {
|
|
84
84
|
//rawAudio = correctDCBias(rawAudio)
|
|
85
85
|
|
|
86
|
-
const
|
|
87
|
-
const
|
|
86
|
+
const targetPeakAmplitude = decibelsToGainFactor(targetPeakDb)
|
|
87
|
+
const maxGainFactor = decibelsToGainFactor(maxIncreaseDb)
|
|
88
88
|
|
|
89
|
-
const
|
|
89
|
+
const peakAmplitude = getAudioPeakAmplitude(rawAudio.audioChannels)
|
|
90
90
|
|
|
91
|
-
const
|
|
91
|
+
const gainFactor = Math.min(targetPeakAmplitude / peakAmplitude, maxGainFactor)
|
|
92
92
|
|
|
93
|
-
return applyGain(rawAudio,
|
|
93
|
+
return applyGain(rawAudio, gainFactor)
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
export function correctDCBias(rawAudio: RawAudio): RawAudio {
|
|
@@ -120,21 +120,19 @@ export function correctDCBias(rawAudio: RawAudio): RawAudio {
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
export function applyGainDecibels(rawAudio: RawAudio, decibelGain: number): RawAudio {
|
|
123
|
-
return applyGain(rawAudio,
|
|
123
|
+
return applyGain(rawAudio, decibelsToGainFactor(decibelGain))
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
export function applyGain(rawAudio: RawAudio,
|
|
126
|
+
export function applyGain(rawAudio: RawAudio, gainFactor: number): RawAudio {
|
|
127
127
|
const outputAudioChannels: Float32Array[] = []
|
|
128
128
|
|
|
129
|
-
const multiplier = gain
|
|
130
|
-
|
|
131
129
|
for (const channelSamples of rawAudio.audioChannels) {
|
|
132
130
|
const sampleCount = channelSamples.length
|
|
133
131
|
|
|
134
132
|
const outputChannelSamples = new Float32Array(sampleCount)
|
|
135
133
|
|
|
136
134
|
for (let i = 0; i < sampleCount; i++) {
|
|
137
|
-
outputChannelSamples[i] = channelSamples[i] *
|
|
135
|
+
outputChannelSamples[i] = channelSamples[i] * gainFactor
|
|
138
136
|
}
|
|
139
137
|
|
|
140
138
|
outputAudioChannels.push(outputChannelSamples)
|
|
@@ -158,19 +156,19 @@ export function downmixToMono(rawAudio: RawAudio): RawAudio {
|
|
|
158
156
|
}
|
|
159
157
|
|
|
160
158
|
export function getAudioPeakDecibels(audioChannels: Float32Array[]) {
|
|
161
|
-
return
|
|
159
|
+
return gainFactorToDecibels(getAudioPeakAmplitude(audioChannels))
|
|
162
160
|
}
|
|
163
161
|
|
|
164
|
-
export function
|
|
165
|
-
let
|
|
162
|
+
export function getAudioPeakAmplitude(audioChannels: Float32Array[]) {
|
|
163
|
+
let maxAmplitude = 0.00001
|
|
166
164
|
|
|
167
165
|
for (const channelSamples of audioChannels) {
|
|
168
166
|
for (const sample of channelSamples) {
|
|
169
|
-
|
|
167
|
+
maxAmplitude = Math.max(maxAmplitude, Math.abs(sample))
|
|
170
168
|
}
|
|
171
169
|
}
|
|
172
170
|
|
|
173
|
-
return
|
|
171
|
+
return maxAmplitude
|
|
174
172
|
}
|
|
175
173
|
|
|
176
174
|
export function mixAudio(rawAudio1: RawAudio, rawAudio2: RawAudio) {
|
|
@@ -252,7 +250,7 @@ export function fadeAudioInOut(rawAudio: RawAudio, fadeTime: number): RawAudio {
|
|
|
252
250
|
const fadeSampleCount = Math.floor(rawAudio.sampleRate * fadeTime)
|
|
253
251
|
const gainReductionPerFrameDecibels = -60 / fadeSampleCount
|
|
254
252
|
|
|
255
|
-
const
|
|
253
|
+
const gainReductionPerFrameFactor = decibelsToGainFactor(gainReductionPerFrameDecibels)
|
|
256
254
|
|
|
257
255
|
const outAudioChannels = rawAudio.audioChannels.map(channel => channel.slice())
|
|
258
256
|
|
|
@@ -261,18 +259,20 @@ export function fadeAudioInOut(rawAudio: RawAudio, fadeTime: number): RawAudio {
|
|
|
261
259
|
continue
|
|
262
260
|
}
|
|
263
261
|
|
|
264
|
-
let
|
|
262
|
+
let factor = 1.0
|
|
265
263
|
|
|
266
264
|
for (let i = fadeSampleCount - 1; i >= 0; i--) {
|
|
267
|
-
channel[i] *=
|
|
268
|
-
|
|
265
|
+
channel[i] *= factor
|
|
266
|
+
|
|
267
|
+
factor *= gainReductionPerFrameFactor
|
|
269
268
|
}
|
|
270
269
|
|
|
271
|
-
|
|
270
|
+
factor = 1.0
|
|
272
271
|
|
|
273
272
|
for (let i = channel.length - fadeSampleCount; i < channel.length; i++) {
|
|
274
|
-
channel[i] *=
|
|
275
|
-
|
|
273
|
+
channel[i] *= factor
|
|
274
|
+
|
|
275
|
+
factor *= gainReductionPerFrameFactor
|
|
276
276
|
}
|
|
277
277
|
}
|
|
278
278
|
|
|
@@ -352,11 +352,11 @@ export async function ensureRawAudio(input: AudioSourceParam, outSampleRate?: nu
|
|
|
352
352
|
// Unit conversion
|
|
353
353
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
354
354
|
|
|
355
|
-
export function
|
|
356
|
-
return
|
|
355
|
+
export function gainFactorToDecibels(gainFactor: number) {
|
|
356
|
+
return gainFactor <= 0.00001 ? -100 : (20.0 * Math.log10(gainFactor))
|
|
357
357
|
}
|
|
358
358
|
|
|
359
|
-
export function
|
|
359
|
+
export function decibelsToGainFactor(decibels: number) {
|
|
360
360
|
return decibels <= -100.0 ? 0 : Math.pow(10, 0.05 * decibels)
|
|
361
361
|
}
|
|
362
362
|
|
package/src/audio/SoxPath.ts
CHANGED
|
@@ -1,24 +1,31 @@
|
|
|
1
|
-
import path from
|
|
2
|
-
import { loadPackage } from
|
|
3
|
-
import { commandExists } from
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import { loadPackage } from '../utilities/PackageManager.js'
|
|
3
|
+
import { commandExists } from '../utilities/Utilities.js'
|
|
4
|
+
import { getGlobalOption } from '../api/GlobalOptions.js'
|
|
4
5
|
|
|
5
6
|
export async function tryResolvingSoxPath() {
|
|
6
|
-
|
|
7
|
+
if (getGlobalOption('soxPath')) {
|
|
8
|
+
return getGlobalOption('soxPath')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const platform = process.platform
|
|
12
|
+
const arch = process.arch
|
|
7
13
|
|
|
8
|
-
if (
|
|
9
|
-
const soxPackagePath = await loadPackage(
|
|
10
|
-
|
|
14
|
+
if (platform === 'win32') {
|
|
15
|
+
const soxPackagePath = await loadPackage('sox-14.4.1a-win32')
|
|
16
|
+
|
|
17
|
+
return path.join(soxPackagePath, 'sox.exe')
|
|
11
18
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
|
|
20
|
+
if (await commandExists('sox')) {
|
|
21
|
+
return 'sox'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (platform === 'linux' && arch === 'x64') {
|
|
25
|
+
const soxPackagePath = await loadPackage('sox-14.4.2-linux-minimal')
|
|
26
|
+
|
|
27
|
+
return path.join(soxPackagePath, 'sox')
|
|
21
28
|
}
|
|
22
29
|
|
|
23
|
-
return
|
|
30
|
+
return undefined
|
|
24
31
|
}
|
|
@@ -6,6 +6,7 @@ import { Logger } from "../utilities/Logger.js"
|
|
|
6
6
|
import { commandExists, logToStderr } from "../utilities/Utilities.js"
|
|
7
7
|
import path from "node:path"
|
|
8
8
|
import { loadPackage } from "../utilities/PackageManager.js"
|
|
9
|
+
import { getGlobalOption } from "../api/GlobalOptions.js"
|
|
9
10
|
|
|
10
11
|
const log = logToStderr
|
|
11
12
|
|
|
@@ -147,25 +148,53 @@ function buildCommandLineArguments(inputFilename: string, outputOptions: FFMpegO
|
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
async function getFFMpegExecutablePath() {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
151
|
+
// If a global option set for the path, use it
|
|
152
|
+
if (getGlobalOption('ffmpegPath')) {
|
|
153
|
+
return getGlobalOption('ffmpegPath')
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// If an 'ffmpeg' command exist in system path, use it
|
|
157
|
+
if (await commandExists('ffmpeg')) {
|
|
158
|
+
return 'ffmpeg'
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Otherwise, download and use an internal ffmpeg package
|
|
162
|
+
const platform = process.platform
|
|
163
|
+
const arch = process.arch
|
|
164
|
+
|
|
165
|
+
let packageName: string
|
|
166
|
+
|
|
167
|
+
if (platform === 'win32' && arch === 'x64') {
|
|
168
|
+
packageName = 'ffmpeg-6.0-win32-x64'
|
|
169
|
+
} else if (platform === 'win32' && arch === 'ia32') {
|
|
170
|
+
packageName = 'ffmpeg-6.0-win32-ia32'
|
|
171
|
+
} else if (platform === 'darwin' && arch === 'x64') {
|
|
172
|
+
packageName = 'ffmpeg-6.0-darwin-x64'
|
|
173
|
+
} else if (platform === 'darwin' && arch === 'arm64') {
|
|
174
|
+
packageName = 'ffmpeg-6.0-darwin-arm64'
|
|
175
|
+
} else if (platform === 'linux' && arch === 'x64') {
|
|
176
|
+
packageName = 'ffmpeg-6.0-linux-x64'
|
|
177
|
+
} else if (platform === 'linux' && arch === 'ia32') {
|
|
178
|
+
packageName = 'ffmpeg-6.0-linux-ia32'
|
|
179
|
+
} else if (platform === 'linux' && arch === 'arm64') {
|
|
180
|
+
packageName = 'ffmpeg-6.0-linux-arm64'
|
|
181
|
+
} else if (platform === 'linux' && arch === 'arm') {
|
|
182
|
+
packageName = 'ffmpeg-6.0-linux-arm'
|
|
183
|
+
} else if (platform === 'freebsd' && arch === 'x64') {
|
|
184
|
+
packageName = 'ffmpeg-6.0-freebsd-x64'
|
|
185
|
+
} else {
|
|
186
|
+
return undefined
|
|
154
187
|
}
|
|
155
|
-
/* else if (process.platform == "darwin" && process.arch == "x64") {
|
|
156
|
-
const ffmpegPackagePath = await loadPackage("ffmpeg-6.0-macos64")
|
|
157
188
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const ffmpegPackagePath = await loadPackage("ffmpeg-6.0-linux-amd64")
|
|
189
|
+
const ffmpegPackagePath = await loadPackage(packageName)
|
|
190
|
+
|
|
191
|
+
let filename = packageName
|
|
162
192
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
return "ffmpeg"
|
|
193
|
+
if (platform === 'win32') {
|
|
194
|
+
filename += '.exe'
|
|
166
195
|
}
|
|
167
196
|
|
|
168
|
-
return
|
|
197
|
+
return path.join(ffmpegPackagePath, filename)
|
|
169
198
|
}
|
|
170
199
|
|
|
171
200
|
export function getDefaultFFMpegOptionsForSpeech(fileExtension: string, customBitrate?: number) {
|
package/src/synthesis/VitsTTS.ts
CHANGED
|
@@ -293,6 +293,12 @@ export const voiceList: SynthesisVoice[] = [
|
|
|
293
293
|
languages: ["de-DE", "de"],
|
|
294
294
|
gender: "male",
|
|
295
295
|
},
|
|
296
|
+
{
|
|
297
|
+
name: "de_DE-mls-medium",
|
|
298
|
+
languages: ["de-DE", "de"],
|
|
299
|
+
gender: "unknown",
|
|
300
|
+
speakerCount: 236
|
|
301
|
+
},
|
|
296
302
|
|
|
297
303
|
{
|
|
298
304
|
name: "el_GR-rapunzelina-low",
|
|
@@ -353,6 +359,12 @@ export const voiceList: SynthesisVoice[] = [
|
|
|
353
359
|
languages: ["en-GB", "en"],
|
|
354
360
|
gender: "female",
|
|
355
361
|
},
|
|
362
|
+
{
|
|
363
|
+
name: "en_GB-cori-high",
|
|
364
|
+
languages: ["en-GB", "en"],
|
|
365
|
+
gender: "female",
|
|
366
|
+
},
|
|
367
|
+
|
|
356
368
|
|
|
357
369
|
{
|
|
358
370
|
name: "en_US-amy-low",
|
|
@@ -443,6 +455,21 @@ export const voiceList: SynthesisVoice[] = [
|
|
|
443
455
|
languages: ["en-US", "en"],
|
|
444
456
|
gender: "female",
|
|
445
457
|
},
|
|
458
|
+
{
|
|
459
|
+
name: "en_US-kristin-medium",
|
|
460
|
+
languages: ["en-US", "en"],
|
|
461
|
+
gender: "female",
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
name: "en_US-ljspeech-high",
|
|
465
|
+
languages: ["en-US", "en"],
|
|
466
|
+
gender: "female",
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
name: "en_US-ljspeech-medium",
|
|
470
|
+
languages: ["en-US", "en"],
|
|
471
|
+
gender: "female",
|
|
472
|
+
},
|
|
446
473
|
|
|
447
474
|
{
|
|
448
475
|
name: "es_ES-carlfm-x_low",
|
|
@@ -476,6 +503,23 @@ export const voiceList: SynthesisVoice[] = [
|
|
|
476
503
|
languages: ["es-MX", "es"],
|
|
477
504
|
gender: "male",
|
|
478
505
|
},
|
|
506
|
+
{
|
|
507
|
+
name: "es_MX-claude-high",
|
|
508
|
+
languages: ["es-MX", "es"],
|
|
509
|
+
gender: "female",
|
|
510
|
+
},
|
|
511
|
+
|
|
512
|
+
{
|
|
513
|
+
name: "fa_IR-amir-medium",
|
|
514
|
+
languages: ["fa_IR", "fa"],
|
|
515
|
+
gender: "male",
|
|
516
|
+
},
|
|
517
|
+
{
|
|
518
|
+
name: "fa_IR-gyro-medium",
|
|
519
|
+
languages: ["fa_IR", "fa"],
|
|
520
|
+
gender: "male",
|
|
521
|
+
},
|
|
522
|
+
|
|
479
523
|
|
|
480
524
|
{
|
|
481
525
|
name: "fi_FI-harri-low",
|
|
@@ -514,6 +558,17 @@ export const voiceList: SynthesisVoice[] = [
|
|
|
514
558
|
gender: "unknown",
|
|
515
559
|
speakerCount: 2
|
|
516
560
|
},
|
|
561
|
+
{
|
|
562
|
+
name: "fr_FR-mls-medium",
|
|
563
|
+
languages: ["fr-FR", "fr"],
|
|
564
|
+
gender: "unknown",
|
|
565
|
+
speakerCount: 125
|
|
566
|
+
},
|
|
567
|
+
{
|
|
568
|
+
name: "fr_FR-tom-medium",
|
|
569
|
+
languages: ["fr-FR", "fr"],
|
|
570
|
+
gender: "male",
|
|
571
|
+
},
|
|
517
572
|
|
|
518
573
|
{
|
|
519
574
|
name: "hu_HU-anna-medium",
|
|
@@ -605,6 +660,12 @@ export const voiceList: SynthesisVoice[] = [
|
|
|
605
660
|
languages: ["nl-NL", "nl"],
|
|
606
661
|
gender: "female",
|
|
607
662
|
},
|
|
663
|
+
{
|
|
664
|
+
name: "nl_NL-mls-medium",
|
|
665
|
+
languages: ["nl-NL", "nl"],
|
|
666
|
+
gender: "unknown",
|
|
667
|
+
speakerCount: 52,
|
|
668
|
+
},
|
|
608
669
|
|
|
609
670
|
{
|
|
610
671
|
name: "nl_BE-nathalie-x_low",
|
|
@@ -705,6 +766,12 @@ export const voiceList: SynthesisVoice[] = [
|
|
|
705
766
|
gender: "female",
|
|
706
767
|
},
|
|
707
768
|
|
|
769
|
+
{
|
|
770
|
+
name: "sl_SI-artur-medium",
|
|
771
|
+
languages: ["sl_SI", "sl"],
|
|
772
|
+
gender: "male",
|
|
773
|
+
},
|
|
774
|
+
|
|
708
775
|
{
|
|
709
776
|
name: "sr_RS-serbski_institut-medium",
|
|
710
777
|
languages: ["sr-RS", "sr"],
|
|
@@ -734,6 +801,11 @@ export const voiceList: SynthesisVoice[] = [
|
|
|
734
801
|
languages: ["tr-TR", "tr"],
|
|
735
802
|
gender: "male",
|
|
736
803
|
},
|
|
804
|
+
{
|
|
805
|
+
name: "tr_TR-fettah-medium",
|
|
806
|
+
languages: ["tr-TR", "tr"],
|
|
807
|
+
gender: "male",
|
|
808
|
+
},
|
|
737
809
|
|
|
738
810
|
{
|
|
739
811
|
name: "uk_UA-lada-x_low",
|