echogarden 0.11.12 → 0.11.13

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 (122) hide show
  1. package/data/schemas/options.json +16 -0
  2. package/dist/api/Alignment.js +2 -2
  3. package/dist/api/Alignment.js.map +1 -1
  4. package/dist/api/Recognition.js +2 -2
  5. package/dist/api/Recognition.js.map +1 -1
  6. package/dist/api/Synthesis.js +5 -4
  7. package/dist/api/Synthesis.js.map +1 -1
  8. package/dist/api/Translation.js +2 -2
  9. package/dist/api/Translation.js.map +1 -1
  10. package/dist/audio/AudioUtilities.d.ts +1 -0
  11. package/dist/audio/AudioUtilities.js +25 -7
  12. package/dist/audio/AudioUtilities.js.map +1 -1
  13. package/dist/cli/CLI.js +2 -2
  14. package/dist/cli/CLI.js.map +1 -1
  15. package/dist/recognition/WhisperSTT.js +2 -2
  16. package/dist/recognition/WhisperSTT.js.map +1 -1
  17. package/dist/subtitles/Subtitles.d.ts +10 -7
  18. package/dist/subtitles/Subtitles.js +268 -207
  19. package/dist/subtitles/Subtitles.js.map +1 -1
  20. package/docs/Options.md +4 -2
  21. package/package.json +7 -6
  22. package/src/alignment/DTWMfccSequenceAlignment.ts +43 -0
  23. package/src/alignment/DTWSequenceAlignment.ts +121 -0
  24. package/src/alignment/DTWSequenceAlignmentWindowed.ts +210 -0
  25. package/src/alignment/LevenshteinSequenceAlignment.ts +126 -0
  26. package/src/alignment/SpeechAlignment.ts +488 -0
  27. package/src/api/API.ts +12 -0
  28. package/src/api/APIOptions.ts +15 -0
  29. package/src/api/Alignment.ts +329 -0
  30. package/src/api/Common.ts +16 -0
  31. package/src/api/Denoising.ts +120 -0
  32. package/src/api/LanguageDetection.ts +286 -0
  33. package/src/api/Recognition.ts +344 -0
  34. package/src/api/Synthesis.ts +1735 -0
  35. package/src/api/Translation.ts +143 -0
  36. package/src/api/Vad.ts +172 -0
  37. package/src/audio/AudioBufferConversion.ts +248 -0
  38. package/src/audio/AudioPlayer.ts +358 -0
  39. package/src/audio/AudioRecorder.ts +91 -0
  40. package/src/audio/AudioUtilities.ts +392 -0
  41. package/src/audio/SoxPath.ts +24 -0
  42. package/src/cli/CLI.ts +1360 -0
  43. package/src/cli/CLIConfigFile.ts +91 -0
  44. package/src/cli/CLILauncher.ts +26 -0
  45. package/src/cli/CLIOptionsSchema.ts +54 -0
  46. package/src/cli/CLIParser.ts +41 -0
  47. package/src/cli/CLIStarter.ts +40 -0
  48. package/src/codecs/FFMpegTranscoder.ts +214 -0
  49. package/src/codecs/TIMITCodec.ts +17 -0
  50. package/src/codecs/WaveCodec.ts +260 -0
  51. package/src/denoising/RNNoise.ts +95 -0
  52. package/src/dsp/BiquadFilter.ts +488 -0
  53. package/src/dsp/FFT.ts +187 -0
  54. package/src/dsp/MFCC.ts +227 -0
  55. package/src/dsp/MelSpectogram.ts +145 -0
  56. package/src/dsp/Rubberband.ts +249 -0
  57. package/src/dsp/Sonic.ts +59 -0
  58. package/src/dsp/SpeexResampler.ts +79 -0
  59. package/src/math/VectorMath.ts +812 -0
  60. package/src/nlp/ChineseSegmentation.ts +68 -0
  61. package/src/nlp/CompromiseNLP.ts +113 -0
  62. package/src/nlp/EspeakPhonemizer.ts +168 -0
  63. package/src/nlp/IPA.ts +139 -0
  64. package/src/nlp/JapaneseSegmentation.ts +53 -0
  65. package/src/nlp/Lexicon.ts +119 -0
  66. package/src/nlp/PhoneConversion.ts +508 -0
  67. package/src/nlp/Segmentation.ts +237 -0
  68. package/src/nlp/TextNormalizer.ts +160 -0
  69. package/src/recognition/AmazonTranscribeSTT.ts +112 -0
  70. package/src/recognition/AzureCognitiveServicesSTT.ts +76 -0
  71. package/src/recognition/GoogleCloudSTT.ts +92 -0
  72. package/src/recognition/SileroSTT.ts +173 -0
  73. package/src/recognition/VoskSTT.ts +112 -0
  74. package/src/recognition/WhisperSTT.ts +1518 -0
  75. package/src/server/Client.ts +297 -0
  76. package/src/server/Server.ts +178 -0
  77. package/src/server/ServerStarter.ts +12 -0
  78. package/src/server/Worker.ts +400 -0
  79. package/src/server/WorkerStarter.ts +38 -0
  80. package/src/speech-language-detection/SileroLanguageDetection.ts +105 -0
  81. package/src/subtitles/Subtitles.ts +478 -0
  82. package/src/synthesis/AwsPollyTTS.ts +78 -0
  83. package/src/synthesis/AzureCognitiveServicesTTS.ts +146 -0
  84. package/src/synthesis/CoquiServerTTS.ts +29 -0
  85. package/src/synthesis/ElevenLabsTTS.ts +104 -0
  86. package/src/synthesis/EspeakTTS.ts +552 -0
  87. package/src/synthesis/FliteTTS.ts +387 -0
  88. package/src/synthesis/GoogleCloudTTS.ts +112 -0
  89. package/src/synthesis/GoogleTranslateTTS.ts +210 -0
  90. package/src/synthesis/MicrosoftEdgeTTS.ts +298 -0
  91. package/src/synthesis/SamTTS.ts +30 -0
  92. package/src/synthesis/SapiTTS.ts +222 -0
  93. package/src/synthesis/StreamlabsPollyTTS.ts +114 -0
  94. package/src/synthesis/SvoxPicoTTS.ts +318 -0
  95. package/src/synthesis/VitsTTS.ts +734 -0
  96. package/src/tests/Test.ts +24 -0
  97. package/src/text-language-detection/FastTextLanguageDetection.ts +53 -0
  98. package/src/text-language-detection/TinyLDLanguageDetection.ts +16 -0
  99. package/src/typings/Fillers.d.ts +41 -0
  100. package/src/utilities/BinaryArrayConversion.ts +159 -0
  101. package/src/utilities/Compression.ts +91 -0
  102. package/src/utilities/FileDownloader.ts +201 -0
  103. package/src/utilities/FileSystem.ts +265 -0
  104. package/src/utilities/Hashing.ts +230 -0
  105. package/src/utilities/Locale.ts +119 -0
  106. package/src/utilities/Logger.ts +72 -0
  107. package/src/utilities/NdArrayUtilities.ts +31 -0
  108. package/src/utilities/ObjectUtilities.ts +169 -0
  109. package/src/utilities/OpenPromise.ts +13 -0
  110. package/src/utilities/PackageManager.ts +97 -0
  111. package/src/utilities/Queue.ts +17 -0
  112. package/src/utilities/RandomGenerator.ts +237 -0
  113. package/src/utilities/SignalChannel.ts +22 -0
  114. package/src/utilities/TarballMaker.ts +68 -0
  115. package/src/utilities/Timeline.ts +231 -0
  116. package/src/utilities/Timer.ts +93 -0
  117. package/src/utilities/Utilities.ts +574 -0
  118. package/src/utilities/WasmMemoryManager.ts +516 -0
  119. package/src/utilities/WebReader.ts +55 -0
  120. package/src/utilities/WikipediaReader.ts +41 -0
  121. package/src/voice-activity-detection/SileroVAD.ts +86 -0
  122. package/src/voice-activity-detection/WebRtcVAD.ts +76 -0
@@ -0,0 +1,91 @@
1
+ import { readAndParseJsonFile, readFile } from "../utilities/FileSystem.js"
2
+
3
+ export async function parseConfigFile(path: string): Promise<ParsedConfigFile> {
4
+ const fileContent = await readFile(path, { encoding: "utf-8" })
5
+
6
+ const lines = fileContent
7
+ .split(/\r?\n/g)
8
+ .map(line => line.trim())
9
+
10
+ const sectionRegExp = /^\s*\[(.*)\]\s*$/
11
+
12
+ const sectionMap = new Map<string, Map<string, string>>()
13
+
14
+ let currentSectionName = ""
15
+
16
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
17
+ const line = lines[lineIndex]
18
+
19
+ if (line.length == 0 || line.startsWith("#")) {
20
+ continue
21
+ }
22
+
23
+ const sectionMatch = sectionRegExp.exec(line)
24
+
25
+ if (sectionMatch) {
26
+ currentSectionName = sectionMatch[1]
27
+ continue
28
+ }
29
+
30
+ if (!(sectionMap.has(currentSectionName))) {
31
+ sectionMap.set(currentSectionName, new Map())
32
+ }
33
+
34
+ const splitPoint = line.indexOf("=")
35
+
36
+ let key: string
37
+ let value: string
38
+
39
+ if (splitPoint == 0) {
40
+ throw new Error(`Invalid empty key on line ${lineIndex + 1}`)
41
+ } else if (splitPoint > 0) {
42
+ key = line.substring(0, splitPoint).trim()
43
+ value = line.substring(splitPoint + 1).trim()
44
+
45
+ if (value) {
46
+ sectionMap.get(currentSectionName)!.set(key, value)
47
+ }
48
+ }
49
+ }
50
+
51
+ return sectionMap
52
+ }
53
+
54
+ export async function parseJSONConfigFile(path: string): Promise<ParsedConfigFile> {
55
+ const parsedJSON = await readAndParseJsonFile(path, true)
56
+
57
+ const result: ParsedConfigFile = new Map()
58
+
59
+ for (const command in parsedJSON) {
60
+ const commandObj = parsedJSON[command]
61
+
62
+ const commandMap = new Map<string, string>()
63
+
64
+ function addFromObject(obj: any, pathPrefix: string): void {
65
+ for (const propertyName in obj) {
66
+ const propertyValue = obj[propertyName]
67
+
68
+ const propertyPath = pathPrefix == "" ? propertyName : `${pathPrefix}.${propertyName}`
69
+
70
+
71
+ if (typeof propertyValue == "object") {
72
+ if (Array.isArray(propertyValue)) {
73
+ commandMap.set(propertyPath, JSON.stringify(propertyValue))
74
+ } else {
75
+ addFromObject(propertyValue, propertyPath)
76
+ }
77
+ } else {
78
+ commandMap.set(propertyPath, propertyValue)
79
+ }
80
+ }
81
+ }
82
+
83
+ addFromObject(commandObj, "")
84
+
85
+ result.set(command, commandMap)
86
+ }
87
+
88
+ return result
89
+ }
90
+
91
+ export type ParsedConfigFile = Map<string, Map<string, string>>
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawn } from 'child_process'
4
+ import { setupUnhandledExceptionListeners } from '../utilities/Utilities.js'
5
+ import { resolveToModuleRootDir } from '../utilities/FileSystem.js'
6
+
7
+ setupUnhandledExceptionListeners()
8
+
9
+ const cmd = process.argv[0]
10
+ const scriptArgs = process.argv.slice(2)
11
+
12
+ const cliScriptPath = resolveToModuleRootDir("dist/cli/CLIStarter.js")
13
+
14
+ const args = [
15
+ "--no-warnings",
16
+ "--no-experimental-fetch",
17
+ "--experimental-wasi-unstable-preview1",
18
+ cliScriptPath,
19
+ ...scriptArgs
20
+ ]
21
+
22
+ const child = spawn(cmd, args, { stdio: "inherit" })
23
+
24
+ child.on("close", code => {
25
+ process.exit(code as number)
26
+ })
@@ -0,0 +1,54 @@
1
+ export function getOptionTypeFromSchema(path: string[], schema: any): SchemaTypeDefinition {
2
+ const definitions = schema.definitions as any
3
+
4
+ let currentObject = definitions
5
+
6
+ for (let keyIndex = 0; keyIndex < path.length; keyIndex++) {
7
+ const isLastKey = keyIndex == path.length - 1
8
+
9
+ const key = path[keyIndex]
10
+
11
+ currentObject = currentObject[key]
12
+
13
+ if (!currentObject) {
14
+ throw new Error(`'${key}' is not a valid property of '${path.slice(0, keyIndex).join(".")}'.`)
15
+ }
16
+
17
+ if ("$ref" in currentObject) {
18
+ const refString = currentObject['$ref'] as string
19
+ const parsedRef = refString.substring(refString.lastIndexOf('/') + 1)
20
+
21
+ currentObject = definitions[parsedRef]
22
+ }
23
+
24
+ const currentObjectType = currentObject['type']
25
+
26
+ if (isLastKey) {
27
+ let possibleValues: any[] | undefined = undefined
28
+
29
+ if ('enum' in currentObject) {
30
+ possibleValues = currentObject['enum'] as any[]
31
+ } else if ('const' in currentObject) {
32
+ possibleValues = [currentObject['const']]
33
+ }
34
+
35
+ const isUnion = ('anyOf' in currentObject)
36
+
37
+ return { type: currentObjectType, enum: possibleValues, isUnion }
38
+ }
39
+
40
+ if (currentObjectType == 'object') {
41
+ currentObject = currentObject['properties']
42
+ } else {
43
+ throw new Error(`Property '${key}' is not an object, but has type ${currentObjectType}`)
44
+ }
45
+ }
46
+
47
+ return {}
48
+ }
49
+
50
+ export type SchemaTypeDefinition = {
51
+ type?: string
52
+ enum?: any[]
53
+ isUnion?: boolean
54
+ }
@@ -0,0 +1,41 @@
1
+ export function parseCLIArguments(command: string, args: string[]): CLIArguments {
2
+ const parsedArgs: CLIArguments = {
3
+ command,
4
+ commandArgs: [],
5
+ options: new Map(),
6
+ }
7
+
8
+ for (let i = 0; i < args.length; i++) {
9
+ const currentArg = args[i]
10
+ const currentArgIsNewOption = currentArg.startsWith("-")
11
+
12
+ if (currentArgIsNewOption) {
13
+ if (!currentArg.startsWith("--")) {
14
+ throw new Error(`'${currentArg}' has a single dash prefix. You should use '-${currentArg}' instead.`)
15
+ }
16
+
17
+ const optionText = currentArg.substring(2)
18
+
19
+ const indexOfEquals = optionText.indexOf("=")
20
+ if (indexOfEquals == 0) {
21
+ throw new Error("An option cannot have an empty name.")
22
+ } else if (indexOfEquals == -1) {
23
+ parsedArgs.options.set(optionText, "")
24
+ } else {
25
+ const key = optionText.substring(0, indexOfEquals)
26
+ const value = optionText.substring(indexOfEquals + 1)
27
+ parsedArgs.options.set(key, value)
28
+ }
29
+ } else {
30
+ parsedArgs.commandArgs.push(currentArg)
31
+ }
32
+ }
33
+
34
+ return parsedArgs
35
+ }
36
+
37
+ export type CLIArguments = {
38
+ command: string
39
+ commandArgs: string[]
40
+ options: Map<string, string>
41
+ }
@@ -0,0 +1,40 @@
1
+ import { resolveToModuleRootDir } from '../utilities/FileSystem.js'
2
+ import { setupProgramTerminationListeners, setupUnhandledExceptionListeners, writeToStderr } from '../utilities/Utilities.js'
3
+ import { Worker, SHARE_ENV } from 'node:worker_threads'
4
+
5
+ setupUnhandledExceptionListeners()
6
+
7
+ setupProgramTerminationListeners(() => {
8
+ writeToStderr('\n')
9
+ process.kill(process.pid, 'SIGKILL')
10
+ })
11
+
12
+ const worker = new Worker(resolveToModuleRootDir("dist/cli/CLI.js"), {
13
+ argv: process.argv.slice(2),
14
+ env: SHARE_ENV
15
+ })
16
+
17
+ worker.postMessage({
18
+ name: 'init',
19
+ stdErrIsTTY: process.stderr.isTTY,
20
+ stdErrHasColors: process.stderr.hasColors ? process.stderr.hasColors() : false
21
+ })
22
+
23
+ process.stdin.on('keypress', (str, key) => {
24
+ worker.postMessage({
25
+ name: 'keypress',
26
+ str,
27
+ key,
28
+ timestamp: Date.now()
29
+ })
30
+ })
31
+
32
+ worker.on("message", (message) => {
33
+ if (message.name == "writeToStdErr") {
34
+ writeToStderr(message.text)
35
+ }
36
+ })
37
+
38
+ worker.on("exit", (err) => {
39
+ process.exit(err ? 1 : 0)
40
+ })
@@ -0,0 +1,214 @@
1
+ import { spawn } from "child_process"
2
+
3
+ import { encodeWaveBuffer, decodeWaveBuffer, RawAudio } from "../audio/AudioUtilities.js"
4
+
5
+ import { Logger } from "../utilities/Logger.js"
6
+ import { commandExists, logToStderr } from "../utilities/Utilities.js"
7
+ import path from "node:path"
8
+ import { loadPackage } from "../utilities/PackageManager.js"
9
+
10
+ const log = logToStderr
11
+
12
+ export type FFMpegOutputOptions = {
13
+ filename?: string
14
+ codec?: string
15
+ format?: string
16
+ sampleRate?: number
17
+ sampleFormat?: "u8" | "s16" | "s32" | "s64" | "flt" | "dbl"
18
+ channelCount?: number
19
+ bitrate?: number
20
+ audioOnly?: boolean
21
+ customOptions?: string[]
22
+ }
23
+
24
+ export async function encodeFromChannels(rawAudio: RawAudio, outputOptions: FFMpegOutputOptions) {
25
+ return transcode(encodeWaveBuffer(rawAudio), outputOptions)
26
+ }
27
+
28
+ export async function decodeToChannels(input: string | Buffer, outSampleRate?: number, outChannelCount?: number) {
29
+ const outputOptions: FFMpegOutputOptions = {
30
+ codec: "pcm_f32le",
31
+ format: "wav",
32
+ sampleRate: outSampleRate,
33
+ channelCount: outChannelCount,
34
+ audioOnly: true
35
+ }
36
+
37
+ const waveAudio = await transcode(input, outputOptions)
38
+
39
+ const { rawAudio } = decodeWaveBuffer(waveAudio, true)
40
+
41
+ return rawAudio
42
+ }
43
+
44
+ export async function transcode(input: string | Buffer, outputOptions: FFMpegOutputOptions) {
45
+ const executablePath = await getFFMpegExecutablePath()
46
+
47
+ if (!executablePath) {
48
+ throw new Error("The ffmpeg utility wasn't found. Please ensure it is available on the system path.")
49
+ }
50
+
51
+ return transcode_CLI(executablePath, input, outputOptions)
52
+ }
53
+
54
+ async function transcode_CLI(ffmpegCommand: string, input: string | Buffer, outputOptions: FFMpegOutputOptions) {
55
+ return new Promise<Buffer>((resolve, reject) => {
56
+ const logger = new Logger()
57
+ logger.start("Transcode with command-line ffmpeg")
58
+
59
+ const args = buildCommandLineArguments(Buffer.isBuffer(input) ? "-" : input, outputOptions)
60
+
61
+ const process = spawn(ffmpegCommand, args)
62
+
63
+ if (Buffer.isBuffer(input)) {
64
+ process.stdin.end(input)
65
+ }
66
+
67
+ const stdOutChunks: Buffer[] = []
68
+ let stdErrOutput = ""
69
+
70
+ process.stdout.on('data', (data) => {
71
+ stdOutChunks.push(data)
72
+ })
73
+
74
+ process.stderr.setEncoding("utf8")
75
+ process.stderr.on('data', (data) => {
76
+ //log(data)
77
+ stdErrOutput += data
78
+ })
79
+
80
+ process.on("error", (e) => {
81
+ reject(e)
82
+ })
83
+
84
+ process.on('close', (exitCode) => {
85
+ if (exitCode == 0) {
86
+ resolve(Buffer.concat(stdOutChunks))
87
+ } else {
88
+ reject(`ffmpeg exited with code ${exitCode}`)
89
+ log(stdErrOutput)
90
+ }
91
+
92
+ logger.end()
93
+ })
94
+ })
95
+ }
96
+
97
+ function buildCommandLineArguments(inputFilename: string, outputOptions: FFMpegOutputOptions) {
98
+ outputOptions = { ...outputOptions }
99
+
100
+ if (!outputOptions.filename) {
101
+ outputOptions.filename = "-"
102
+ }
103
+
104
+ const args: string[] = []
105
+
106
+ args.push(
107
+ `-i`, `${inputFilename}`,
108
+ )
109
+
110
+ if (outputOptions.audioOnly) {
111
+ args.push(`-map`, `a`)
112
+ }
113
+
114
+ if (outputOptions.codec) {
115
+ args.push(`-c:a`, `${outputOptions.codec}`)
116
+ }
117
+
118
+ if (outputOptions.format) {
119
+ args.push(`-f:a`, `${outputOptions.format}`)
120
+ }
121
+
122
+ if (outputOptions.sampleRate) {
123
+ args.push(`-ar`, `${outputOptions.sampleRate}`)
124
+ }
125
+
126
+ if (outputOptions.sampleFormat) {
127
+ args.push(`-sample_fmt`, `${outputOptions.sampleFormat}`)
128
+ }
129
+
130
+ if (outputOptions.channelCount) {
131
+ args.push(`-ac`, `${outputOptions.channelCount}`)
132
+ }
133
+
134
+ if (outputOptions.bitrate) {
135
+ args.push(`-ab`, `${outputOptions.bitrate}k`)
136
+ }
137
+
138
+ args.push(`-y`)
139
+
140
+ if (outputOptions.customOptions) {
141
+ args.push(...outputOptions.customOptions)
142
+ }
143
+
144
+ args.push(outputOptions.filename)
145
+
146
+ return args
147
+ }
148
+
149
+ async function getFFMpegExecutablePath() {
150
+ if (process.platform == "win32") {
151
+ const ffmpegPackagePath = await loadPackage("ffmpeg-6.0-essentials-win64")
152
+
153
+ return path.join(ffmpegPackagePath, "ffmpeg.exe")
154
+ }
155
+ /* else if (process.platform == "darwin" && process.arch == "x64") {
156
+ const ffmpegPackagePath = await loadPackage("ffmpeg-6.0-macos64")
157
+
158
+ return path.join(ffmpegPackagePath, "ffmpeg")
159
+ }
160
+ */ else if (process.platform == "linux" && process.arch == "x64") {
161
+ const ffmpegPackagePath = await loadPackage("ffmpeg-6.0-linux-amd64")
162
+
163
+ return path.join(ffmpegPackagePath, "ffmpeg")
164
+ } else if (await commandExists("ffmpeg")) {
165
+ return "ffmpeg"
166
+ }
167
+
168
+ return undefined
169
+ }
170
+
171
+ export function getDefaultFFMpegOptionsForSpeech(fileExtension: string, customBitrate?: number) {
172
+ let ffmpegOptions: FFMpegOutputOptions
173
+
174
+ if (fileExtension == "mp3") {
175
+ ffmpegOptions = {
176
+ format: "mp3",
177
+ codec: "libmp3lame",
178
+ bitrate: 64,
179
+ customOptions: []
180
+ }
181
+ } else if (fileExtension == "opus") {
182
+ ffmpegOptions = {
183
+ codec: "libopus",
184
+ bitrate: 48,
185
+ customOptions: []
186
+ }
187
+ } else if (fileExtension == "m4a") {
188
+ ffmpegOptions = {
189
+ format: "mp4",
190
+ codec: "aac",
191
+ bitrate: 48,
192
+ customOptions: ["-profile:a", "aac_low", "-movflags", "frag_keyframe+empty_moov"]
193
+ }
194
+ } else if (fileExtension == "ogg") {
195
+ ffmpegOptions = {
196
+ codec: "libvorbis",
197
+ bitrate: 48,
198
+ customOptions: []
199
+ }
200
+ } else if (fileExtension == "flac") {
201
+ ffmpegOptions = {
202
+ format: "flac",
203
+ customOptions: ["-compression_level", "6"]
204
+ }
205
+ } else {
206
+ throw new Error(`Unsupported codec extension: '${fileExtension}'`)
207
+ }
208
+
209
+ if (customBitrate != null) {
210
+ ffmpegOptions.bitrate = customBitrate
211
+ }
212
+
213
+ return ffmpegOptions
214
+ }
@@ -0,0 +1,17 @@
1
+ import { decodeToChannels } from "../audio/AudioBufferConversion.js"
2
+ import { readFile } from "../utilities/FileSystem.js"
3
+ import { SampleFormat } from "./WaveCodec.js"
4
+
5
+ export async function decodeTimitAudioFile(filename: string) {
6
+ return decodeTimitAudio(await readFile(filename))
7
+ }
8
+
9
+ export function decodeTimitAudio(data: Buffer) {
10
+ if (data.subarray(0, 16).toString("ascii") != "NIST_1A\n 1024\n") {
11
+ throw new Error("Data is not a valid TIMIT audio file")
12
+ }
13
+
14
+ const pcm = data.subarray(1024)
15
+
16
+ return { audioChannels: decodeToChannels(pcm, 1, 16, SampleFormat.PCM), sampleRate: 16000 }
17
+ }