echogarden 0.9.2 → 0.10.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 +3 -7
- package/data/schemas/options.json +57 -26
- package/dist/api/Alignment.d.ts +4 -4
- package/dist/api/Alignment.js +31 -31
- package/dist/api/Alignment.js.map +1 -1
- package/dist/api/Denoising.d.ts +9 -3
- package/dist/api/Denoising.js +9 -9
- package/dist/api/Denoising.js.map +1 -1
- package/dist/api/LanguageDetection.d.ts +13 -17
- package/dist/api/LanguageDetection.js +13 -17
- package/dist/api/LanguageDetection.js.map +1 -1
- package/dist/api/Recognition.d.ts +4 -4
- package/dist/api/Recognition.js +10 -12
- package/dist/api/Recognition.js.map +1 -1
- package/dist/api/Synthesis.d.ts +10 -6
- package/dist/api/Synthesis.js +61 -25
- package/dist/api/Synthesis.js.map +1 -1
- package/dist/api/Translation.d.ts +4 -4
- package/dist/api/Translation.js +11 -12
- package/dist/api/Translation.js.map +1 -1
- package/dist/api/Vad.d.ts +5 -8
- package/dist/api/Vad.js +12 -14
- package/dist/api/Vad.js.map +1 -1
- package/dist/audio/AudioPlayer.d.ts +1 -1
- package/dist/audio/AudioPlayer.js +12 -14
- package/dist/audio/AudioPlayer.js.map +1 -1
- package/dist/audio/AudioUtilities.d.ts +2 -0
- package/dist/audio/AudioUtilities.js +28 -0
- package/dist/audio/AudioUtilities.js.map +1 -1
- package/dist/audio/SoxPath.js +4 -0
- package/dist/audio/SoxPath.js.map +1 -1
- package/dist/cli/CLI.js +84 -120
- package/dist/cli/CLI.js.map +1 -1
- package/dist/codecs/FFMpegTranscoder.d.ts +1 -0
- package/dist/codecs/FFMpegTranscoder.js +48 -2
- package/dist/codecs/FFMpegTranscoder.js.map +1 -1
- package/dist/denoising/RNNoise.js.map +1 -1
- package/dist/dsp/MFCC.js.map +1 -1
- package/dist/nlp/JapaneseSegmentation.js +3 -2
- package/dist/nlp/JapaneseSegmentation.js.map +1 -1
- package/dist/recognition/WhisperSTT.js +2 -2
- package/dist/recognition/WhisperSTT.js.map +1 -1
- package/dist/server/Client.d.ts +10 -8
- package/dist/server/Client.js +56 -59
- package/dist/server/Client.js.map +1 -1
- package/dist/server/Server.js +23 -12
- package/dist/server/Server.js.map +1 -1
- package/dist/server/Worker.d.ts +28 -11
- package/dist/server/Worker.js +44 -11
- package/dist/server/Worker.js.map +1 -1
- package/dist/speech-language-detection/SileroLanguageDetection.js +2 -2
- package/dist/speech-language-detection/SileroLanguageDetection.js.map +1 -1
- package/dist/synthesis/EspeakTTS.js +2 -3
- package/dist/synthesis/EspeakTTS.js.map +1 -1
- package/dist/synthesis/GoogleTranslateTTS.js.map +1 -1
- package/dist/synthesis/MicrosoftEdgeTTS.js +1 -1
- package/dist/synthesis/MicrosoftEdgeTTS.js.map +1 -1
- package/dist/synthesis/VitsTTS.js.map +1 -1
- package/dist/utilities/Logger.d.ts +2 -2
- package/dist/utilities/Logger.js +7 -9
- package/dist/utilities/Logger.js.map +1 -1
- package/dist/utilities/ObjectUtilities.js +4 -1
- package/dist/utilities/ObjectUtilities.js.map +1 -1
- package/dist/utilities/PackageManager.js +3 -1
- package/dist/utilities/PackageManager.js.map +1 -1
- package/dist/utilities/Timeline.js.map +1 -1
- package/dist/utilities/Utilities.d.ts +1 -0
- package/dist/utilities/Utilities.js +6 -0
- package/dist/utilities/Utilities.js.map +1 -1
- package/docs/API.md +188 -0
- package/docs/Options.md +30 -23
- package/docs/Server.md +110 -0
- package/docs/Tasklist.md +4 -8
- package/package.json +7 -7
package/dist/cli/CLI.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as API from '../api/API.js';
|
|
2
2
|
import { parseCLIArguments } from './CLIParser.js';
|
|
3
|
-
import { convertHtmlToText, formatIntegerWithLeadingZeros, formatListWithQuotedElements, getWithDefault,
|
|
3
|
+
import { convertHtmlToText, formatIntegerWithLeadingZeros, formatListWithQuotedElements, getWithDefault, setupUnhandledExceptionListeners, splitFilenameOnExtendedExtension, stringifyAndFormatJson } from "../utilities/Utilities.js";
|
|
4
4
|
import { getOptionTypeFromSchema } from "./CLIOptionsSchema.js";
|
|
5
5
|
import { parseConfigFile, parseJSONConfigFile } from "./CLIConfigFile.js";
|
|
6
6
|
import chalk from 'chalk';
|
|
@@ -8,12 +8,12 @@ import { applyGainDecibels, encodeWaveBuffer, getEmptyRawAudio, normalizeAudioLe
|
|
|
8
8
|
import { subtitlesToText, subtitlesToTimeline, timelineToSubtitles } from "../subtitles/Subtitles.js";
|
|
9
9
|
import { Logger } from "../utilities/Logger.js";
|
|
10
10
|
import { isMainThread, parentPort } from 'node:worker_threads';
|
|
11
|
-
import { encodeFromChannels } from "../codecs/FFMpegTranscoder.js";
|
|
11
|
+
import { encodeFromChannels, getDefaultFFMpegOptionsForSpeech } from "../codecs/FFMpegTranscoder.js";
|
|
12
12
|
import path, { parse as parsePath } from "node:path";
|
|
13
13
|
import { splitToParagraphs, splitToWords, wordCharacterPattern } from "../nlp/Segmentation.js";
|
|
14
14
|
import { playAudioSamples, playAudioWithWordTimeline } from "../audio/AudioPlayer.js";
|
|
15
15
|
import { extendDeep } from "../utilities/ObjectUtilities.js";
|
|
16
|
-
import { addTimeOffsetToTimeline, addWordTextOffsetsToTimeline, roundTimelineProperties
|
|
16
|
+
import { addTimeOffsetToTimeline, addWordTextOffsetsToTimeline, roundTimelineProperties } from "../utilities/Timeline.js";
|
|
17
17
|
import { ensureDir, existsSync, getLowercaseFileExtension, readAndParseJsonFile, readFile, readdir, resolveToModuleRootDir, writeFileSafe } from '../utilities/FileSystem.js';
|
|
18
18
|
import { formatLanguageCodeWithName, getShortLanguageCode } from '../utilities/Locale.js';
|
|
19
19
|
import { ensureAndGetPackagesDir, getVersionTagFromPackageName, loadPackage, resolveVersionTagForUnversionedPackageName } from '../utilities/PackageManager.js';
|
|
@@ -21,7 +21,7 @@ import { removePackage } from '../utilities/PackageManager.js';
|
|
|
21
21
|
import { appName } from '../api/Common.js';
|
|
22
22
|
import { startWebSocketServer } from '../server/Server.js';
|
|
23
23
|
import { OpenPromise } from '../utilities/OpenPromise.js';
|
|
24
|
-
const log = logToStderr
|
|
24
|
+
//const log = logToStderr
|
|
25
25
|
async function startIfInWorkerThread() {
|
|
26
26
|
if (isMainThread || !parentPort) {
|
|
27
27
|
return;
|
|
@@ -43,17 +43,18 @@ async function startIfInWorkerThread() {
|
|
|
43
43
|
start(process.argv.slice(2));
|
|
44
44
|
}
|
|
45
45
|
export async function start(processArgs) {
|
|
46
|
+
const logger = new Logger();
|
|
46
47
|
let args;
|
|
47
48
|
try {
|
|
48
49
|
const packageData = await readAndParseJsonFile(resolveToModuleRootDir("package.json"));
|
|
49
|
-
log(chalk.magentaBright(`Echogarden v${packageData.version}\n`));
|
|
50
|
+
logger.log(chalk.magentaBright(`Echogarden v${packageData.version}\n`));
|
|
50
51
|
const command = processArgs[0];
|
|
51
52
|
if (!command || command == "help") {
|
|
52
|
-
log(`Supported operations:\n\n${commandHelp.join("\n")}`);
|
|
53
|
+
logger.log(`Supported operations:\n\n${commandHelp.join("\n")}`);
|
|
53
54
|
process.exit(0);
|
|
54
55
|
}
|
|
55
56
|
if (command == "--help") {
|
|
56
|
-
log(`There's no command called '--help'. Did you mean to run 'echogarden help'?`);
|
|
57
|
+
logger.log(`There's no command called '--help'. Did you mean to run 'echogarden help'?`);
|
|
57
58
|
process.exit(0);
|
|
58
59
|
}
|
|
59
60
|
args = parseCLIArguments(command, processArgs.slice(1));
|
|
@@ -92,7 +93,7 @@ export async function start(processArgs) {
|
|
|
92
93
|
}
|
|
93
94
|
}
|
|
94
95
|
catch (e) {
|
|
95
|
-
|
|
96
|
+
logger.logTitledMessage(`Error`, e.message, chalk.redBright);
|
|
96
97
|
process.exit(1);
|
|
97
98
|
}
|
|
98
99
|
let debugMode = false;
|
|
@@ -105,10 +106,10 @@ export async function start(processArgs) {
|
|
|
105
106
|
}
|
|
106
107
|
catch (e) {
|
|
107
108
|
if (debugMode) {
|
|
108
|
-
log(e);
|
|
109
|
+
logger.log(e);
|
|
109
110
|
}
|
|
110
111
|
else {
|
|
111
|
-
|
|
112
|
+
logger.logTitledMessage(`Error`, e.message, chalk.redBright);
|
|
112
113
|
}
|
|
113
114
|
process.exit(1);
|
|
114
115
|
}
|
|
@@ -148,8 +149,11 @@ const commandHelp = [
|
|
|
148
149
|
` Uninstall one or more Echogarden packages\n`,
|
|
149
150
|
`${executableName} ${chalk.magentaBright('list-packages')} [options...]`,
|
|
150
151
|
` List installed Echogarden packages\n`,
|
|
152
|
+
`${executableName} ${chalk.magentaBright('serve')} [options...]`,
|
|
153
|
+
` Start a server\n`,
|
|
151
154
|
];
|
|
152
155
|
async function startWithArgs(parsedArgs) {
|
|
156
|
+
const logger = new Logger();
|
|
153
157
|
switch (parsedArgs.command) {
|
|
154
158
|
case 'speak':
|
|
155
159
|
case 'speak-file':
|
|
@@ -215,13 +219,13 @@ async function startWithArgs(parsedArgs) {
|
|
|
215
219
|
break;
|
|
216
220
|
}
|
|
217
221
|
default: {
|
|
218
|
-
|
|
222
|
+
logger.logTitledMessage(`Unknown command`, parsedArgs.command, chalk.redBright);
|
|
219
223
|
process.exit(1);
|
|
220
224
|
}
|
|
221
225
|
}
|
|
222
226
|
}
|
|
223
227
|
async function speak(command, commandArgs, cliOptions) {
|
|
224
|
-
const
|
|
228
|
+
const logger = new Logger();
|
|
225
229
|
const mainArg = commandArgs[0];
|
|
226
230
|
const outputFilenames = commandArgs.slice(1);
|
|
227
231
|
if (mainArg == undefined) {
|
|
@@ -318,10 +322,10 @@ async function speak(command, commandArgs, cliOptions) {
|
|
|
318
322
|
}
|
|
319
323
|
async function onSegment(segmentData) {
|
|
320
324
|
if (includesPartPattern) {
|
|
321
|
-
|
|
325
|
+
logger.start("Writing output files for segment");
|
|
322
326
|
}
|
|
323
327
|
await writeOutputFilesForSegment(outputFilenames, segmentData.index, segmentData.total, segmentData.audio, segmentData.timeline, segmentData.transcript, segmentData.language, allowOverwrite);
|
|
324
|
-
|
|
328
|
+
logger.end();
|
|
325
329
|
if (options.play) {
|
|
326
330
|
let gainAmount = -3 - segmentData.peakDecibelsSoFar;
|
|
327
331
|
//gainAmount = Math.min(gainAmount, 0)
|
|
@@ -330,12 +334,15 @@ async function speak(command, commandArgs, cliOptions) {
|
|
|
330
334
|
await playAudioWithWordTimeline(audioWithAddedGain, segmentWordTimeline, segmentData.transcript);
|
|
331
335
|
}
|
|
332
336
|
}
|
|
333
|
-
|
|
337
|
+
if (options.outputAudioFormat?.codec) {
|
|
338
|
+
options.outputAudioFormat.codec = undefined;
|
|
339
|
+
}
|
|
340
|
+
const { audio: synthesizedAudio, timeline } = await API.synthesize(textSegments, options, onSegment, undefined);
|
|
334
341
|
if (plainText) {
|
|
335
342
|
addWordTextOffsetsToTimeline(timeline, plainText);
|
|
336
343
|
}
|
|
337
344
|
if (outputFilenames.length > 0) {
|
|
338
|
-
|
|
345
|
+
logger.start("\nWriting output files");
|
|
339
346
|
}
|
|
340
347
|
for (const outputFile of outputFilenames) {
|
|
341
348
|
const partPatternMatch = outputFile.match(segmentFilenamePattern);
|
|
@@ -345,10 +352,10 @@ async function speak(command, commandArgs, cliOptions) {
|
|
|
345
352
|
const fileSaver = getFileSaver(outputFile, allowOverwrite);
|
|
346
353
|
await fileSaver(synthesizedAudio, timeline, textSegments.join("\n\n"), options.subtitles);
|
|
347
354
|
}
|
|
348
|
-
|
|
355
|
+
logger.end();
|
|
349
356
|
}
|
|
350
357
|
async function transcribe(commandArgs, cliOptions) {
|
|
351
|
-
const
|
|
358
|
+
const logger = new Logger();
|
|
352
359
|
const sourceFilename = commandArgs[0];
|
|
353
360
|
const outputFilenames = commandArgs.slice(1);
|
|
354
361
|
if (sourceFilename == undefined) {
|
|
@@ -366,11 +373,9 @@ async function transcribe(commandArgs, cliOptions) {
|
|
|
366
373
|
const options = await cliOptionsMapToOptionsObject(cliOptions, "RecognitionOptions", additionalOptionsSchema);
|
|
367
374
|
const allowOverwrite = getWithDefault(options.overwrite, overwriteByDefault);
|
|
368
375
|
const { includesPartPattern } = await checkOutputFilenames(outputFilenames, true, true, true);
|
|
369
|
-
const { transcript, timeline
|
|
370
|
-
const { segmentTimeline } = await wordTimelineToSegmentSentenceTimeline(wordTimeline, transcript, language, 'single', 'preserve');
|
|
371
|
-
addWordTextOffsetsToTimeline(segmentTimeline, transcript);
|
|
376
|
+
const { transcript, timeline, wordTimeline, inputRawAudio, language } = await API.recognize(sourceFilename, options);
|
|
372
377
|
if (outputFilenames.length > 0) {
|
|
373
|
-
|
|
378
|
+
logger.start("\nWriting output files");
|
|
374
379
|
}
|
|
375
380
|
for (const outputFile of outputFilenames) {
|
|
376
381
|
const partPatternMatch = outputFile.match(segmentFilenamePattern);
|
|
@@ -378,16 +383,16 @@ async function transcribe(commandArgs, cliOptions) {
|
|
|
378
383
|
continue;
|
|
379
384
|
}
|
|
380
385
|
const fileSaver = getFileSaver(outputFile, allowOverwrite);
|
|
381
|
-
await fileSaver(
|
|
386
|
+
await fileSaver(inputRawAudio, timeline, transcript, options.subtitles);
|
|
382
387
|
}
|
|
383
|
-
|
|
388
|
+
logger.end();
|
|
384
389
|
if (options.play) {
|
|
385
|
-
const normalizedAudio = normalizeAudioLevel(
|
|
390
|
+
const normalizedAudio = normalizeAudioLevel(inputRawAudio);
|
|
386
391
|
await playAudioWithWordTimeline(normalizedAudio, wordTimeline, transcript);
|
|
387
392
|
}
|
|
388
393
|
}
|
|
389
394
|
async function align(commandArgs, cliOptions) {
|
|
390
|
-
const
|
|
395
|
+
const logger = new Logger();
|
|
391
396
|
const audioFilename = commandArgs[0];
|
|
392
397
|
const outputFilenames = commandArgs.slice(2);
|
|
393
398
|
if (audioFilename == undefined) {
|
|
@@ -427,18 +432,16 @@ async function align(commandArgs, cliOptions) {
|
|
|
427
432
|
const options = await cliOptionsMapToOptionsObject(cliOptions, "AlignmentOptions", additionalOptionsSchema);
|
|
428
433
|
const allowOverwrite = getWithDefault(options.overwrite, overwriteByDefault);
|
|
429
434
|
const { includesPartPattern } = await checkOutputFilenames(outputFilenames, true, true, true);
|
|
430
|
-
const {
|
|
431
|
-
const { segmentTimeline } = await wordTimelineToSegmentSentenceTimeline(wordTimeline, transcript, language, options.plainText?.paragraphBreaks, options.plainText?.whitespace);
|
|
432
|
-
addWordTextOffsetsToTimeline(segmentTimeline, transcript);
|
|
435
|
+
const { timeline, wordTimeline, transcript, language, inputRawAudio } = await API.align(audioFilename, text, options);
|
|
433
436
|
if (outputFilenames.length > 0) {
|
|
434
|
-
|
|
437
|
+
logger.start("\nWriting output files");
|
|
435
438
|
}
|
|
436
439
|
if (includesPartPattern) {
|
|
437
|
-
for (let segmentIndex = 0; segmentIndex <
|
|
438
|
-
const segmentEntry =
|
|
439
|
-
const segmentAudio = sliceRawAudioByTime(
|
|
440
|
+
for (let segmentIndex = 0; segmentIndex < timeline.length; segmentIndex++) {
|
|
441
|
+
const segmentEntry = timeline[segmentIndex];
|
|
442
|
+
const segmentAudio = sliceRawAudioByTime(inputRawAudio, segmentEntry.startTime, segmentEntry.endTime);
|
|
440
443
|
const sentenceTimeline = addTimeOffsetToTimeline(segmentEntry.timeline, -segmentEntry.startTime);
|
|
441
|
-
await writeOutputFilesForSegment(outputFilenames, segmentIndex,
|
|
444
|
+
await writeOutputFilesForSegment(outputFilenames, segmentIndex, timeline.length, segmentAudio, sentenceTimeline, segmentEntry.text, language, allowOverwrite);
|
|
442
445
|
}
|
|
443
446
|
}
|
|
444
447
|
for (const outputFile of outputFilenames) {
|
|
@@ -447,17 +450,16 @@ async function align(commandArgs, cliOptions) {
|
|
|
447
450
|
continue;
|
|
448
451
|
}
|
|
449
452
|
const fileSaver = getFileSaver(outputFile, allowOverwrite);
|
|
450
|
-
await fileSaver(
|
|
453
|
+
await fileSaver(inputRawAudio, timeline, transcript, options.subtitles);
|
|
451
454
|
}
|
|
452
|
-
|
|
455
|
+
logger.end();
|
|
453
456
|
if (options.play) {
|
|
454
|
-
const normalizedAudio = normalizeAudioLevel(
|
|
455
|
-
const wordTimeline = segmentTimeline.flatMap(segmentEntry => segmentEntry.timeline).flatMap(sentenceEntry => sentenceEntry.timeline);
|
|
457
|
+
const normalizedAudio = normalizeAudioLevel(inputRawAudio);
|
|
456
458
|
await playAudioWithWordTimeline(normalizedAudio, wordTimeline, transcript);
|
|
457
459
|
}
|
|
458
460
|
}
|
|
459
461
|
async function translateSpeech(commandArgs, cliOptions) {
|
|
460
|
-
const
|
|
462
|
+
const logger = new Logger();
|
|
461
463
|
const inputFilename = commandArgs[0];
|
|
462
464
|
const outputFilenames = commandArgs.slice(1);
|
|
463
465
|
if (inputFilename == undefined) {
|
|
@@ -475,11 +477,9 @@ async function translateSpeech(commandArgs, cliOptions) {
|
|
|
475
477
|
const options = await cliOptionsMapToOptionsObject(cliOptions, "SpeechTranslationOptions", additionalOptionsSchema);
|
|
476
478
|
const allowOverwrite = getWithDefault(options.overwrite, overwriteByDefault);
|
|
477
479
|
await checkOutputFilenames(outputFilenames, true, true, true);
|
|
478
|
-
const { transcript, timeline
|
|
479
|
-
const { segmentTimeline } = await wordTimelineToSegmentSentenceTimeline(wordTimeline, transcript, targetLanguage, 'single', 'preserve');
|
|
480
|
-
addWordTextOffsetsToTimeline(segmentTimeline, transcript);
|
|
480
|
+
const { transcript, timeline, wordTimeline, sourceLanguage, targetLanguage, inputRawAudio } = await API.translateSpeech(inputFilename, options);
|
|
481
481
|
if (outputFilenames.length > 0) {
|
|
482
|
-
|
|
482
|
+
logger.start("\nWriting output files");
|
|
483
483
|
}
|
|
484
484
|
for (const outputFile of outputFilenames) {
|
|
485
485
|
const partPatternMatch = outputFile.match(segmentFilenamePattern);
|
|
@@ -487,16 +487,16 @@ async function translateSpeech(commandArgs, cliOptions) {
|
|
|
487
487
|
continue;
|
|
488
488
|
}
|
|
489
489
|
const fileSaver = getFileSaver(outputFile, allowOverwrite);
|
|
490
|
-
await fileSaver(
|
|
490
|
+
await fileSaver(inputRawAudio, timeline, transcript, options.subtitles);
|
|
491
491
|
}
|
|
492
|
-
|
|
492
|
+
logger.end();
|
|
493
493
|
if (options.play) {
|
|
494
|
-
const normalizedAudio = normalizeAudioLevel(
|
|
494
|
+
const normalizedAudio = normalizeAudioLevel(inputRawAudio);
|
|
495
495
|
await playAudioWithWordTimeline(normalizedAudio, wordTimeline, transcript);
|
|
496
496
|
}
|
|
497
497
|
}
|
|
498
498
|
async function detectLanguage(commandArgs, cliOptions, mode) {
|
|
499
|
-
const
|
|
499
|
+
const logger = new Logger();
|
|
500
500
|
const inputFilePath = commandArgs[0];
|
|
501
501
|
const outputFilenames = commandArgs.slice(1);
|
|
502
502
|
if (!existsSync(inputFilePath)) {
|
|
@@ -532,11 +532,11 @@ async function detectLanguage(commandArgs, cliOptions, mode) {
|
|
|
532
532
|
const options = await cliOptionsMapToOptionsObject(cliOptions, "SpeechLanguageDetectionOptions", additionalOptionsSchema);
|
|
533
533
|
allowOverwrite = getWithDefault(options.overwrite, overwriteByDefault);
|
|
534
534
|
await checkOutputFilenames(outputFilenames, false, true, false);
|
|
535
|
-
const { detectedLanguage, detectedLanguageProbabilities } = await API.
|
|
535
|
+
const { detectedLanguage, detectedLanguageProbabilities } = await API.detectSpeechLanguage(inputFilePath, options);
|
|
536
536
|
results = detectedLanguageProbabilities;
|
|
537
537
|
}
|
|
538
538
|
if (outputFilenames.length > 0) {
|
|
539
|
-
|
|
539
|
+
logger.start("\nWriting output files");
|
|
540
540
|
const resultsAsText = results.map(result => `${formatLanguageCodeWithName(result.language)}: ${result.probability.toFixed(5)}`).join("\n");
|
|
541
541
|
for (const outputFile of outputFilenames) {
|
|
542
542
|
const fileSaver = getFileSaver(outputFile, allowOverwrite);
|
|
@@ -545,13 +545,13 @@ async function detectLanguage(commandArgs, cliOptions, mode) {
|
|
|
545
545
|
}
|
|
546
546
|
else {
|
|
547
547
|
const resultsAsText = results.slice(0, 10).map(result => `${formatLanguageCodeWithName(result.language)}: ${result.probability.toFixed(5)}`).join("\n");
|
|
548
|
-
log("");
|
|
549
|
-
log(resultsAsText);
|
|
548
|
+
logger.log("");
|
|
549
|
+
logger.log(resultsAsText);
|
|
550
550
|
}
|
|
551
|
-
|
|
551
|
+
logger.end();
|
|
552
552
|
}
|
|
553
553
|
async function detectVoiceActivity(commandArgs, cliOptions) {
|
|
554
|
-
const
|
|
554
|
+
const logger = new Logger();
|
|
555
555
|
const audioFilename = commandArgs[0];
|
|
556
556
|
const outputFilenames = commandArgs.slice(1);
|
|
557
557
|
if (audioFilename == undefined) {
|
|
@@ -569,9 +569,9 @@ async function detectVoiceActivity(commandArgs, cliOptions) {
|
|
|
569
569
|
const options = await cliOptionsMapToOptionsObject(cliOptions, "VADOptions", additionalOptionsSchema);
|
|
570
570
|
const allowOverwrite = getWithDefault(options.overwrite, overwriteByDefault);
|
|
571
571
|
await checkOutputFilenames(outputFilenames, true, true, false);
|
|
572
|
-
const { timeline,
|
|
572
|
+
const { timeline, inputRawAudio } = await API.detectVoiceActivity(audioFilename, options);
|
|
573
573
|
if (outputFilenames.length > 0) {
|
|
574
|
-
|
|
574
|
+
logger.start("\nWriting output files");
|
|
575
575
|
}
|
|
576
576
|
for (const outputFile of outputFilenames) {
|
|
577
577
|
const partPatternMatch = outputFile.match(segmentFilenamePattern);
|
|
@@ -579,16 +579,19 @@ async function detectVoiceActivity(commandArgs, cliOptions) {
|
|
|
579
579
|
continue;
|
|
580
580
|
}
|
|
581
581
|
const fileSaver = getFileSaver(outputFile, allowOverwrite);
|
|
582
|
-
await fileSaver(
|
|
582
|
+
await fileSaver(inputRawAudio, timeline, "");
|
|
583
583
|
}
|
|
584
|
-
|
|
584
|
+
logger.end();
|
|
585
585
|
if (options.play) {
|
|
586
|
-
const normalizedAudio = normalizeAudioLevel(
|
|
587
|
-
|
|
586
|
+
const normalizedAudio = normalizeAudioLevel(inputRawAudio);
|
|
587
|
+
const timelineToPlay = timeline.map(entry => {
|
|
588
|
+
return { ...entry, type: "word" };
|
|
589
|
+
});
|
|
590
|
+
await playAudioWithWordTimeline(normalizedAudio, timelineToPlay);
|
|
588
591
|
}
|
|
589
592
|
}
|
|
590
593
|
async function denoise(commandArgs, cliOptions) {
|
|
591
|
-
const
|
|
594
|
+
const logger = new Logger();
|
|
592
595
|
const audioFilename = commandArgs[0];
|
|
593
596
|
const outputFilenames = commandArgs.slice(1);
|
|
594
597
|
if (audioFilename == undefined) {
|
|
@@ -606,17 +609,17 @@ async function denoise(commandArgs, cliOptions) {
|
|
|
606
609
|
const options = await cliOptionsMapToOptionsObject(cliOptions, "DenoisingOptions", additionalOptionsSchema);
|
|
607
610
|
const allowOverwrite = getWithDefault(options.overwrite, overwriteByDefault);
|
|
608
611
|
await checkOutputFilenames(outputFilenames, true, false, false);
|
|
609
|
-
const
|
|
612
|
+
const { denoisedAudio } = await API.denoise(audioFilename, options);
|
|
610
613
|
if (outputFilenames.length > 0) {
|
|
611
|
-
|
|
614
|
+
logger.start("\nWriting output files");
|
|
612
615
|
}
|
|
613
616
|
for (const filename of outputFilenames) {
|
|
614
617
|
const fileSaver = getFileSaver(filename, allowOverwrite);
|
|
615
|
-
await fileSaver(
|
|
618
|
+
await fileSaver(denoisedAudio, [], "");
|
|
616
619
|
}
|
|
617
|
-
|
|
620
|
+
logger.end();
|
|
618
621
|
if (options.play) {
|
|
619
|
-
await playAudioSamples(
|
|
622
|
+
await playAudioSamples(denoisedAudio);
|
|
620
623
|
}
|
|
621
624
|
}
|
|
622
625
|
async function listEngines(commandArgs, cliOptions) {
|
|
@@ -686,12 +689,12 @@ async function listEngines(commandArgs, cliOptions) {
|
|
|
686
689
|
logger.logTitledMessage('Description', engine.description);
|
|
687
690
|
logger.logTitledMessage('Type', engine.type);
|
|
688
691
|
if (index < engines.length - 1) {
|
|
689
|
-
log("");
|
|
692
|
+
logger.log("");
|
|
690
693
|
}
|
|
691
694
|
}
|
|
692
695
|
}
|
|
693
696
|
async function listTTSVoices(commandArgs, cliOptions) {
|
|
694
|
-
const
|
|
697
|
+
const logger = new Logger();
|
|
695
698
|
const targetEngine = commandArgs[0];
|
|
696
699
|
const outputFilenames = commandArgs.slice(1);
|
|
697
700
|
if (!targetEngine) {
|
|
@@ -718,7 +721,7 @@ async function listTTSVoices(commandArgs, cliOptions) {
|
|
|
718
721
|
return entryText;
|
|
719
722
|
}).join("\n\n");
|
|
720
723
|
if (outputFilenames.length > 0) {
|
|
721
|
-
|
|
724
|
+
logger.start("\nWriting output files");
|
|
722
725
|
for (const filename of outputFilenames) {
|
|
723
726
|
const fileSaver = getFileSaver(filename, allowOverwrite);
|
|
724
727
|
const { default: stripAnsi } = await import('strip-ansi');
|
|
@@ -727,11 +730,12 @@ async function listTTSVoices(commandArgs, cliOptions) {
|
|
|
727
730
|
}
|
|
728
731
|
}
|
|
729
732
|
else {
|
|
730
|
-
log(voiceListText);
|
|
733
|
+
logger.log(voiceListText);
|
|
731
734
|
}
|
|
732
|
-
|
|
735
|
+
logger.end();
|
|
733
736
|
}
|
|
734
737
|
async function installPackages(commandArgs, cliOptions) {
|
|
738
|
+
const logger = new Logger();
|
|
735
739
|
if (commandArgs.length == 0) {
|
|
736
740
|
throw new Error("No package names specified");
|
|
737
741
|
}
|
|
@@ -741,20 +745,21 @@ async function installPackages(commandArgs, cliOptions) {
|
|
|
741
745
|
await loadPackage(packageName);
|
|
742
746
|
}
|
|
743
747
|
catch (e) {
|
|
744
|
-
log(`Failed installing package ${packageName}: ${e}`);
|
|
748
|
+
logger.log(`Failed installing package ${packageName}: ${e}`);
|
|
745
749
|
failedPackageNames.push(packageName);
|
|
746
750
|
}
|
|
747
751
|
}
|
|
748
752
|
if (failedPackageNames.length > 0) {
|
|
749
753
|
if (failedPackageNames.length == 1) {
|
|
750
|
-
log(`The package ${failedPackageNames[0]} failed to install`);
|
|
754
|
+
logger.log(`The package ${failedPackageNames[0]} failed to install`);
|
|
751
755
|
}
|
|
752
756
|
else {
|
|
753
|
-
log(`The packages ${failedPackageNames.join(', ')} failed to install`);
|
|
757
|
+
logger.log(`The packages ${failedPackageNames.join(', ')} failed to install`);
|
|
754
758
|
}
|
|
755
759
|
}
|
|
756
760
|
}
|
|
757
761
|
async function uninstallPackages(commandArgs, cliOptions) {
|
|
762
|
+
const logger = new Logger();
|
|
758
763
|
if (commandArgs.length == 0) {
|
|
759
764
|
throw new Error("No package names specified");
|
|
760
765
|
}
|
|
@@ -764,15 +769,16 @@ async function uninstallPackages(commandArgs, cliOptions) {
|
|
|
764
769
|
await removePackage(packageName);
|
|
765
770
|
}
|
|
766
771
|
catch (e) {
|
|
767
|
-
log(`Failed uninstalling package ${packageName}: ${e}`);
|
|
772
|
+
logger.log(`Failed uninstalling package ${packageName}: ${e}`);
|
|
768
773
|
failedPackageNames.push(packageName);
|
|
769
774
|
}
|
|
770
775
|
}
|
|
771
776
|
if (failedPackageNames.length > 0) {
|
|
772
|
-
log(`The packages ${failedPackageNames.join(', ')} failed to uninstall`);
|
|
777
|
+
logger.log(`The packages ${failedPackageNames.join(', ')} failed to uninstall`);
|
|
773
778
|
}
|
|
774
779
|
}
|
|
775
780
|
async function listPackages(commandArgs, cliOptions) {
|
|
781
|
+
const logger = new Logger();
|
|
776
782
|
const packagesDir = await ensureAndGetPackagesDir();
|
|
777
783
|
const installedPackageNames = await readdir(packagesDir);
|
|
778
784
|
const installedPackageNamesFormatted = installedPackageNames.map(packageName => {
|
|
@@ -790,8 +796,8 @@ async function listPackages(commandArgs, cliOptions) {
|
|
|
790
796
|
}
|
|
791
797
|
});
|
|
792
798
|
installedPackageNamesFormatted.sort();
|
|
793
|
-
log(`Total of ${installedPackageNamesFormatted.length} packages installed in '${packagesDir}'\n`);
|
|
794
|
-
log(installedPackageNamesFormatted.join("\n"));
|
|
799
|
+
logger.log(`Total of ${installedPackageNamesFormatted.length} packages installed in '${packagesDir}'\n`);
|
|
800
|
+
logger.log(installedPackageNamesFormatted.join("\n"));
|
|
795
801
|
}
|
|
796
802
|
async function startServer(commandArgs, cliOptions) {
|
|
797
803
|
const options = await cliOptionsMapToOptionsObject(cliOptions, "ServerOptions");
|
|
@@ -1003,49 +1009,7 @@ function getFileSaver(outputFilePath, allowOverwrite) {
|
|
|
1003
1009
|
}
|
|
1004
1010
|
else if (supportedOutputMediaFileExtensions.includes(fileExtension)) {
|
|
1005
1011
|
fileSaver = async (audio) => {
|
|
1006
|
-
|
|
1007
|
-
if (fileExtension == "mp3") {
|
|
1008
|
-
ffmpegOptions = {
|
|
1009
|
-
format: "mp3",
|
|
1010
|
-
codec: "libmp3lame",
|
|
1011
|
-
bitrate: 64,
|
|
1012
|
-
customOptions: []
|
|
1013
|
-
};
|
|
1014
|
-
}
|
|
1015
|
-
else if (fileExtension == "opus") {
|
|
1016
|
-
ffmpegOptions = {
|
|
1017
|
-
codec: "libopus",
|
|
1018
|
-
bitrate: 48,
|
|
1019
|
-
customOptions: []
|
|
1020
|
-
};
|
|
1021
|
-
}
|
|
1022
|
-
else if (fileExtension == "m4a") {
|
|
1023
|
-
ffmpegOptions = {
|
|
1024
|
-
format: "mp4",
|
|
1025
|
-
codec: "aac",
|
|
1026
|
-
bitrate: 48,
|
|
1027
|
-
customOptions: ["-profile:a", "aac_low", "-movflags", "frag_keyframe+empty_moov"]
|
|
1028
|
-
};
|
|
1029
|
-
}
|
|
1030
|
-
else if (fileExtension == "ogg") {
|
|
1031
|
-
ffmpegOptions = {
|
|
1032
|
-
codec: "libvorbis",
|
|
1033
|
-
bitrate: 48,
|
|
1034
|
-
customOptions: []
|
|
1035
|
-
};
|
|
1036
|
-
}
|
|
1037
|
-
else if (fileExtension == "flac") {
|
|
1038
|
-
ffmpegOptions = {
|
|
1039
|
-
format: "flac",
|
|
1040
|
-
customOptions: ["-compression_level", "6"]
|
|
1041
|
-
};
|
|
1042
|
-
}
|
|
1043
|
-
else {
|
|
1044
|
-
ffmpegOptions = {
|
|
1045
|
-
format: fileExtension,
|
|
1046
|
-
customOptions: []
|
|
1047
|
-
};
|
|
1048
|
-
}
|
|
1012
|
+
const ffmpegOptions = getDefaultFFMpegOptionsForSpeech(fileExtension);
|
|
1049
1013
|
ffmpegOptions.filename = outputFilePath;
|
|
1050
1014
|
await ensureDir(fileDir);
|
|
1051
1015
|
await encodeFromChannels(audio, ffmpegOptions);
|