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.
Files changed (74) hide show
  1. package/README.md +3 -7
  2. package/data/schemas/options.json +57 -26
  3. package/dist/api/Alignment.d.ts +4 -4
  4. package/dist/api/Alignment.js +31 -31
  5. package/dist/api/Alignment.js.map +1 -1
  6. package/dist/api/Denoising.d.ts +9 -3
  7. package/dist/api/Denoising.js +9 -9
  8. package/dist/api/Denoising.js.map +1 -1
  9. package/dist/api/LanguageDetection.d.ts +13 -17
  10. package/dist/api/LanguageDetection.js +13 -17
  11. package/dist/api/LanguageDetection.js.map +1 -1
  12. package/dist/api/Recognition.d.ts +4 -4
  13. package/dist/api/Recognition.js +10 -12
  14. package/dist/api/Recognition.js.map +1 -1
  15. package/dist/api/Synthesis.d.ts +10 -6
  16. package/dist/api/Synthesis.js +61 -25
  17. package/dist/api/Synthesis.js.map +1 -1
  18. package/dist/api/Translation.d.ts +4 -4
  19. package/dist/api/Translation.js +11 -12
  20. package/dist/api/Translation.js.map +1 -1
  21. package/dist/api/Vad.d.ts +5 -8
  22. package/dist/api/Vad.js +12 -14
  23. package/dist/api/Vad.js.map +1 -1
  24. package/dist/audio/AudioPlayer.d.ts +1 -1
  25. package/dist/audio/AudioPlayer.js +12 -14
  26. package/dist/audio/AudioPlayer.js.map +1 -1
  27. package/dist/audio/AudioUtilities.d.ts +2 -0
  28. package/dist/audio/AudioUtilities.js +28 -0
  29. package/dist/audio/AudioUtilities.js.map +1 -1
  30. package/dist/audio/SoxPath.js +4 -0
  31. package/dist/audio/SoxPath.js.map +1 -1
  32. package/dist/cli/CLI.js +84 -120
  33. package/dist/cli/CLI.js.map +1 -1
  34. package/dist/codecs/FFMpegTranscoder.d.ts +1 -0
  35. package/dist/codecs/FFMpegTranscoder.js +48 -2
  36. package/dist/codecs/FFMpegTranscoder.js.map +1 -1
  37. package/dist/denoising/RNNoise.js.map +1 -1
  38. package/dist/dsp/MFCC.js.map +1 -1
  39. package/dist/nlp/JapaneseSegmentation.js +3 -2
  40. package/dist/nlp/JapaneseSegmentation.js.map +1 -1
  41. package/dist/recognition/WhisperSTT.js +2 -2
  42. package/dist/recognition/WhisperSTT.js.map +1 -1
  43. package/dist/server/Client.d.ts +10 -8
  44. package/dist/server/Client.js +56 -59
  45. package/dist/server/Client.js.map +1 -1
  46. package/dist/server/Server.js +23 -12
  47. package/dist/server/Server.js.map +1 -1
  48. package/dist/server/Worker.d.ts +28 -11
  49. package/dist/server/Worker.js +44 -11
  50. package/dist/server/Worker.js.map +1 -1
  51. package/dist/speech-language-detection/SileroLanguageDetection.js +2 -2
  52. package/dist/speech-language-detection/SileroLanguageDetection.js.map +1 -1
  53. package/dist/synthesis/EspeakTTS.js +2 -3
  54. package/dist/synthesis/EspeakTTS.js.map +1 -1
  55. package/dist/synthesis/GoogleTranslateTTS.js.map +1 -1
  56. package/dist/synthesis/MicrosoftEdgeTTS.js +1 -1
  57. package/dist/synthesis/MicrosoftEdgeTTS.js.map +1 -1
  58. package/dist/synthesis/VitsTTS.js.map +1 -1
  59. package/dist/utilities/Logger.d.ts +2 -2
  60. package/dist/utilities/Logger.js +7 -9
  61. package/dist/utilities/Logger.js.map +1 -1
  62. package/dist/utilities/ObjectUtilities.js +4 -1
  63. package/dist/utilities/ObjectUtilities.js.map +1 -1
  64. package/dist/utilities/PackageManager.js +3 -1
  65. package/dist/utilities/PackageManager.js.map +1 -1
  66. package/dist/utilities/Timeline.js.map +1 -1
  67. package/dist/utilities/Utilities.d.ts +1 -0
  68. package/dist/utilities/Utilities.js +6 -0
  69. package/dist/utilities/Utilities.js.map +1 -1
  70. package/docs/API.md +188 -0
  71. package/docs/Options.md +30 -23
  72. package/docs/Server.md +110 -0
  73. package/docs/Tasklist.md +4 -8
  74. 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, logToStderr, setupUnhandledExceptionListeners, splitFilenameOnExtendedExtension, stringifyAndFormatJson } from "../utilities/Utilities.js";
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, wordTimelineToSegmentSentenceTimeline } from "../utilities/Timeline.js";
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
- log(`Error: ${e.message}`);
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
- log(`Error: ${e.message}`);
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
- log(`Unknown command: ${parsedArgs.command}`);
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 progressLogger = new Logger();
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
- progressLogger.start("Writing output files for segment");
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
- progressLogger.end();
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
- const { synthesizedAudio, timeline } = await API.synthesizeSegments(textSegments, options, onSegment, undefined);
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
- progressLogger.start("\nWriting output files");
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
- progressLogger.end();
355
+ logger.end();
349
356
  }
350
357
  async function transcribe(commandArgs, cliOptions) {
351
- const progressLogger = new Logger();
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: wordTimeline, rawAudio, language } = await API.recognizeFile(sourceFilename, options);
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
- progressLogger.start("\nWriting output files");
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(rawAudio, segmentTimeline, transcript, options.subtitles);
386
+ await fileSaver(inputRawAudio, timeline, transcript, options.subtitles);
382
387
  }
383
- progressLogger.end();
388
+ logger.end();
384
389
  if (options.play) {
385
- const normalizedAudio = normalizeAudioLevel(rawAudio);
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 progressLogger = new Logger();
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 { wordTimeline, rawAudio, transcript, language } = await API.alignFile(audioFilename, text, options);
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
- progressLogger.start("\nWriting output files");
437
+ logger.start("\nWriting output files");
435
438
  }
436
439
  if (includesPartPattern) {
437
- for (let segmentIndex = 0; segmentIndex < segmentTimeline.length; segmentIndex++) {
438
- const segmentEntry = segmentTimeline[segmentIndex];
439
- const segmentAudio = sliceRawAudioByTime(rawAudio, segmentEntry.startTime, segmentEntry.endTime);
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, segmentTimeline.length, segmentAudio, sentenceTimeline, segmentEntry.text, language, allowOverwrite);
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(rawAudio, segmentTimeline, transcript, options.subtitles);
453
+ await fileSaver(inputRawAudio, timeline, transcript, options.subtitles);
451
454
  }
452
- progressLogger.end();
455
+ logger.end();
453
456
  if (options.play) {
454
- const normalizedAudio = normalizeAudioLevel(rawAudio);
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 progressLogger = new Logger();
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: wordTimeline, rawAudio, targetLanguage } = await API.translateSpeechFile(inputFilename, options);
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
- progressLogger.start("\nWriting output files");
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(rawAudio, segmentTimeline, transcript, options.subtitles);
490
+ await fileSaver(inputRawAudio, timeline, transcript, options.subtitles);
491
491
  }
492
- progressLogger.end();
492
+ logger.end();
493
493
  if (options.play) {
494
- const normalizedAudio = normalizeAudioLevel(rawAudio);
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 progressLogger = new Logger();
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.detectSpeechFileLanguage(inputFilePath, options);
535
+ const { detectedLanguage, detectedLanguageProbabilities } = await API.detectSpeechLanguage(inputFilePath, options);
536
536
  results = detectedLanguageProbabilities;
537
537
  }
538
538
  if (outputFilenames.length > 0) {
539
- progressLogger.start("\nWriting output files");
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
- progressLogger.end();
551
+ logger.end();
552
552
  }
553
553
  async function detectVoiceActivity(commandArgs, cliOptions) {
554
- const progressLogger = new Logger();
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, rawAudio } = await API.detectFileVoiceActivity(audioFilename, options);
572
+ const { timeline, inputRawAudio } = await API.detectVoiceActivity(audioFilename, options);
573
573
  if (outputFilenames.length > 0) {
574
- progressLogger.start("\nWriting output files");
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(rawAudio, timeline, "");
582
+ await fileSaver(inputRawAudio, timeline, "");
583
583
  }
584
- progressLogger.end();
584
+ logger.end();
585
585
  if (options.play) {
586
- const normalizedAudio = normalizeAudioLevel(rawAudio);
587
- await playAudioWithWordTimeline(normalizedAudio, timeline);
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 progressLogger = new Logger();
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 outputAudio = await API.denoiseFile(audioFilename, options);
612
+ const { denoisedAudio } = await API.denoise(audioFilename, options);
610
613
  if (outputFilenames.length > 0) {
611
- progressLogger.start("\nWriting output files");
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(outputAudio, [], "");
618
+ await fileSaver(denoisedAudio, [], "");
616
619
  }
617
- progressLogger.end();
620
+ logger.end();
618
621
  if (options.play) {
619
- await playAudioSamples(outputAudio);
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 progressLogger = new Logger();
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
- progressLogger.start("\nWriting output files");
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
- progressLogger.end();
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
- let ffmpegOptions;
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);