echogarden 1.8.7 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -18
- package/data/schemas/options.json +52 -47
- package/dist/alignment/DTWSequenceAlignment.d.ts +1 -1
- package/dist/alignment/DTWSequenceAlignment.js +5 -5
- package/dist/alignment/DTWSequenceAlignment.js.map +1 -1
- package/dist/alignment/SpeechAlignment.js +6 -6
- package/dist/alignment/SpeechAlignment.js.map +1 -1
- package/dist/api/Alignment.d.ts +1 -1
- package/dist/api/Alignment.js +49 -11
- package/dist/api/Alignment.js.map +1 -1
- package/dist/api/Denoising.d.ts +4 -2
- package/dist/api/Denoising.js +65 -13
- package/dist/api/Denoising.js.map +1 -1
- package/dist/api/SourceSeparation.js +9 -6
- package/dist/api/SourceSeparation.js.map +1 -1
- package/dist/api/SpeechLanguageDetection.js +4 -4
- package/dist/api/SpeechLanguageDetection.js.map +1 -1
- package/dist/api/Synthesis.d.ts +2 -2
- package/dist/api/Synthesis.js +10 -7
- package/dist/api/Synthesis.js.map +1 -1
- package/dist/api/VoiceActivityDetection.js +2 -2
- package/dist/api/VoiceActivityDetection.js.map +1 -1
- package/dist/audio/AudioBufferConversion.d.ts +2 -2
- package/dist/audio/AudioBufferConversion.js +77 -43
- package/dist/audio/AudioBufferConversion.js.map +1 -1
- package/dist/audio/AudioPlayer.d.ts +7 -5
- package/dist/audio/AudioPlayer.js +135 -49
- package/dist/audio/AudioPlayer.js.map +1 -1
- package/dist/audio/AudioUtilities.d.ts +4 -3
- package/dist/audio/AudioUtilities.js +30 -15
- package/dist/audio/AudioUtilities.js.map +1 -1
- package/dist/audio/SoxPath.js +3 -3
- package/dist/audio/SoxPath.js.map +1 -1
- package/dist/build-tools/MakeTarballsForInstalledPackages.js +3 -3
- package/dist/build-tools/MakeTarballsForInstalledPackages.js.map +1 -1
- package/dist/cli/CLI.js +30 -36
- package/dist/cli/CLI.js.map +1 -1
- package/dist/cli/CLIConfigFile.js +2 -2
- package/dist/cli/CLIConfigFile.js.map +1 -1
- package/dist/cli/CLIOptions.d.ts +2 -0
- package/dist/cli/CLIOptions.js +1 -1
- package/dist/cli/CLIOptions.js.map +1 -1
- package/dist/codecs/FFMpegTranscoder.d.ts +3 -3
- package/dist/codecs/FFMpegTranscoder.js +6 -6
- package/dist/codecs/FFMpegTranscoder.js.map +1 -1
- package/dist/codecs/TIMITCodec.d.ts +1 -1
- package/dist/codecs/TIMITCodec.js +4 -3
- package/dist/codecs/TIMITCodec.js.map +1 -1
- package/dist/codecs/WaveCodec.d.ts +3 -3
- package/dist/codecs/WaveCodec.js +37 -34
- package/dist/codecs/WaveCodec.js.map +1 -1
- package/dist/data-structures/DynamicTypedArray.d.ts +17 -0
- package/dist/data-structures/DynamicTypedArray.js +47 -0
- package/dist/data-structures/DynamicTypedArray.js.map +1 -0
- package/dist/data-structures/Queue.js.map +1 -0
- package/dist/data-structures/WindowedList.js.map +1 -0
- package/dist/denoising/NSNet2.d.ts +26 -0
- package/dist/denoising/NSNet2.js +128 -0
- package/dist/denoising/NSNet2.js.map +1 -0
- package/dist/denoising/RNNoise.js +3 -3
- package/dist/denoising/RNNoise.js.map +1 -1
- package/dist/dsp/FFT.d.ts +6 -1
- package/dist/dsp/FFT.js +17 -0
- package/dist/dsp/FFT.js.map +1 -1
- package/dist/encodings/Ascii.d.ts +10 -0
- package/dist/encodings/Ascii.js +41 -0
- package/dist/encodings/Ascii.js.map +1 -0
- package/dist/encodings/Base64.d.ts +5 -0
- package/dist/encodings/Base64.js +114 -0
- package/dist/encodings/Base64.js.map +1 -0
- package/dist/encodings/Hex.d.ts +3 -0
- package/dist/encodings/Hex.js +52 -0
- package/dist/encodings/Hex.js.map +1 -0
- package/dist/{utilities → encodings}/LEB128.d.ts +1 -1
- package/dist/{utilities → encodings}/LEB128.js +4 -4
- package/dist/encodings/LEB128.js.map +1 -0
- package/dist/{utilities → encodings}/LPVarInt.d.ts +1 -1
- package/dist/{utilities → encodings}/LPVarInt.js +6 -6
- package/dist/encodings/LPVarInt.js.map +1 -0
- package/dist/encodings/TextEncodingsCommon.d.ts +4 -0
- package/dist/encodings/TextEncodingsCommon.js +2 -0
- package/dist/encodings/TextEncodingsCommon.js.map +1 -0
- package/dist/encodings/Utf16.d.ts +10 -0
- package/dist/encodings/Utf16.js +36 -0
- package/dist/encodings/Utf16.js.map +1 -0
- package/dist/encodings/Utf32.d.ts +9 -0
- package/dist/encodings/Utf32.js +44 -0
- package/dist/encodings/Utf32.js.map +1 -0
- package/dist/encodings/Utf8.d.ts +10 -0
- package/dist/encodings/Utf8.js +97 -0
- package/dist/encodings/Utf8.js.map +1 -0
- package/dist/math/MedianFilter.js.map +1 -1
- package/dist/nlp/JapaneseSegmentation.js +2 -2
- package/dist/nlp/JapaneseSegmentation.js.map +1 -1
- package/dist/recognition/GoogleCloudSTT.js +2 -1
- package/dist/recognition/GoogleCloudSTT.js.map +1 -1
- package/dist/recognition/SileroSTT.js +3 -3
- package/dist/recognition/SileroSTT.js.map +1 -1
- package/dist/recognition/VoskSTT.js +3 -3
- package/dist/recognition/VoskSTT.js.map +1 -1
- package/dist/recognition/WhisperCppSTT.js +4 -5
- package/dist/recognition/WhisperCppSTT.js.map +1 -1
- package/dist/recognition/WhisperSTT.js +8 -7
- package/dist/recognition/WhisperSTT.js.map +1 -1
- package/dist/server/Client.d.ts +1 -1
- package/dist/server/Client.js +2 -1
- package/dist/server/Client.js.map +1 -1
- package/dist/server/Server.js +5 -4
- package/dist/server/Server.js.map +1 -1
- package/dist/server/Worker.js +1 -1
- package/dist/server/Worker.js.map +1 -1
- package/dist/source-separation/MDXNetSourceSeparation.js +1 -1
- package/dist/source-separation/MDXNetSourceSeparation.js.map +1 -1
- package/dist/speech-search/DTWSpeechSearch.d.ts +1 -1
- package/dist/speech-search/DTWSpeechSearch.js +25 -6
- package/dist/speech-search/DTWSpeechSearch.js.map +1 -1
- package/dist/subtitles/Subtitles.js +2 -2
- package/dist/subtitles/Subtitles.js.map +1 -1
- package/dist/synthesis/AzureCognitiveServicesTTS.js +17 -18
- package/dist/synthesis/AzureCognitiveServicesTTS.js.map +1 -1
- package/dist/synthesis/CoquiServerTTS.js +1 -1
- package/dist/synthesis/CoquiServerTTS.js.map +1 -1
- package/dist/synthesis/ElevenlabsTTS.js +1 -1
- package/dist/synthesis/ElevenlabsTTS.js.map +1 -1
- package/dist/synthesis/EspeakTTS.d.ts +0 -1
- package/dist/synthesis/EspeakTTS.js +0 -9
- package/dist/synthesis/EspeakTTS.js.map +1 -1
- package/dist/synthesis/FliteTTS.js +7 -8
- package/dist/synthesis/FliteTTS.js.map +1 -1
- package/dist/synthesis/GoogleCloudTTS.d.ts +1 -1
- package/dist/synthesis/GoogleCloudTTS.js +2 -1
- package/dist/synthesis/GoogleCloudTTS.js.map +1 -1
- package/dist/synthesis/GoogleTranslateTTS.d.ts +1 -1
- package/dist/synthesis/GoogleTranslateTTS.js +4 -3
- package/dist/synthesis/GoogleTranslateTTS.js.map +1 -1
- package/dist/synthesis/MicrosoftEdgeTTS.js +32 -7
- package/dist/synthesis/MicrosoftEdgeTTS.js.map +1 -1
- package/dist/synthesis/SapiTTS.js +1 -1
- package/dist/synthesis/SapiTTS.js.map +1 -1
- package/dist/synthesis/StreamlabsPollyTTS.d.ts +1 -1
- package/dist/synthesis/StreamlabsPollyTTS.js +1 -1
- package/dist/synthesis/StreamlabsPollyTTS.js.map +1 -1
- package/dist/synthesis/SvoxPicoTTS.js +6 -6
- package/dist/synthesis/SvoxPicoTTS.js.map +1 -1
- package/dist/synthesis/VitsTTS.js +2 -2
- package/dist/synthesis/VitsTTS.js.map +1 -1
- package/dist/tests/Test.js +1 -47
- package/dist/tests/Test.js.map +1 -1
- package/dist/text-language-detection/FastTextLanguageDetection.js +2 -2
- package/dist/text-language-detection/FastTextLanguageDetection.js.map +1 -1
- package/dist/typings/TypedArray.d.ts +4 -0
- package/dist/typings/TypedArray.js +2 -0
- package/dist/typings/TypedArray.js.map +1 -0
- package/dist/utilities/BinaryArrayConversion.d.ts +12 -22
- package/dist/utilities/BinaryArrayConversion.js +40 -90
- package/dist/utilities/BinaryArrayConversion.js.map +1 -1
- package/dist/utilities/BinaryUtilities.d.ts +13 -0
- package/dist/utilities/BinaryUtilities.js +113 -0
- package/dist/utilities/BinaryUtilities.js.map +1 -0
- package/dist/utilities/Compression.js +7 -6
- package/dist/utilities/Compression.js.map +1 -1
- package/dist/utilities/FileDownloader.js +26 -47
- package/dist/utilities/FileDownloader.js.map +1 -1
- package/dist/utilities/FileReader.d.ts +14 -0
- package/dist/utilities/FileReader.js +71 -0
- package/dist/utilities/FileReader.js.map +1 -0
- package/dist/utilities/FileSystem.d.ts +19 -16
- package/dist/utilities/FileSystem.js +170 -109
- package/dist/utilities/FileSystem.js.map +1 -1
- package/dist/utilities/FileWriter.d.ts +12 -0
- package/dist/utilities/FileWriter.js +60 -0
- package/dist/utilities/FileWriter.js.map +1 -0
- package/dist/utilities/NpmUtilities.d.ts +1 -0
- package/dist/utilities/NpmUtilities.js +14 -0
- package/dist/utilities/NpmUtilities.js.map +1 -0
- package/dist/utilities/PackageManager.js +7 -5
- package/dist/utilities/PackageManager.js.map +1 -1
- package/dist/utilities/PathUtilities.d.ts +8 -0
- package/dist/utilities/PathUtilities.js +28 -7
- package/dist/utilities/PathUtilities.js.map +1 -1
- package/dist/utilities/SignalChannel.d.ts +3 -1
- package/dist/utilities/SignalChannel.js +9 -5
- package/dist/utilities/SignalChannel.js.map +1 -1
- package/dist/utilities/StringBuilder.d.ts +10 -0
- package/dist/utilities/StringBuilder.js +39 -0
- package/dist/utilities/StringBuilder.js.map +1 -0
- package/dist/utilities/StringUtilities.js.map +1 -1
- package/dist/utilities/TarballMaker.js +6 -7
- package/dist/utilities/TarballMaker.js.map +1 -1
- package/dist/utilities/Timer.js +2 -2
- package/dist/utilities/Timer.js.map +1 -1
- package/dist/utilities/Utilities.d.ts +11 -5
- package/dist/utilities/Utilities.js +69 -33
- package/dist/utilities/Utilities.js.map +1 -1
- package/dist/utilities/VirtualFileReadStream.d.ts +3 -3
- package/dist/utilities/VirtualFileReadStream.js.map +1 -1
- package/dist/utilities/WasmMemoryManager.d.ts +5 -2
- package/dist/utilities/WasmMemoryManager.js +15 -4
- package/dist/utilities/WasmMemoryManager.js.map +1 -1
- package/docs/API.md +1 -1
- package/docs/Licenses.md +3 -0
- package/docs/Options.md +9 -3
- package/docs/Tasklist.md +0 -1
- package/docs/Technical.md +2 -2
- package/package.json +20 -22
- package/src/alignment/DTWSequenceAlignment.ts +5 -5
- package/src/alignment/SpeechAlignment.ts +6 -6
- package/src/api/Alignment.ts +65 -12
- package/src/api/Denoising.ts +97 -17
- package/src/api/SourceSeparation.ts +11 -8
- package/src/api/SpeechLanguageDetection.ts +4 -4
- package/src/api/Synthesis.ts +15 -11
- package/src/api/VoiceActivityDetection.ts +2 -2
- package/src/audio/AudioBufferConversion.ts +84 -45
- package/src/audio/AudioPlayer.ts +155 -53
- package/src/audio/AudioUtilities.ts +38 -22
- package/src/audio/SoxPath.ts +3 -3
- package/src/build-tools/MakeTarballsForInstalledPackages.ts +3 -3
- package/src/cli/CLI.ts +30 -36
- package/src/cli/CLIConfigFile.ts +2 -2
- package/src/cli/CLIOptions.ts +4 -1
- package/src/codecs/FFMpegTranscoder.ts +11 -11
- package/src/codecs/TIMITCodec.ts +5 -4
- package/src/codecs/WaveCodec.ts +42 -39
- package/src/data-structures/DynamicTypedArray.ts +64 -0
- package/src/denoising/NSNet2.ts +182 -0
- package/src/denoising/RNNoise.ts +3 -3
- package/src/dsp/FFT.ts +21 -1
- package/src/encodings/Ascii.ts +60 -0
- package/src/encodings/Base64.ts +157 -0
- package/src/encodings/Hex.ts +67 -0
- package/src/{utilities → encodings}/LEB128.ts +4 -4
- package/src/{utilities → encodings}/LPVarInt.ts +6 -6
- package/src/encodings/TextEncodingsCommon.ts +4 -0
- package/src/encodings/Utf16.ts +52 -0
- package/src/encodings/Utf32.ts +62 -0
- package/src/encodings/Utf8.ts +117 -0
- package/src/math/MedianFilter.ts +0 -2
- package/src/nlp/JapaneseSegmentation.ts +2 -2
- package/src/recognition/GoogleCloudSTT.ts +2 -1
- package/src/recognition/SileroSTT.ts +3 -3
- package/src/recognition/VoskSTT.ts +3 -3
- package/src/recognition/WhisperCppSTT.ts +4 -5
- package/src/recognition/WhisperSTT.ts +8 -7
- package/src/server/Client.ts +4 -3
- package/src/server/Server.ts +6 -5
- package/src/server/Worker.ts +1 -1
- package/src/source-separation/MDXNetSourceSeparation.ts +1 -1
- package/src/speech-search/DTWSpeechSearch.ts +36 -7
- package/src/subtitles/Subtitles.ts +3 -3
- package/src/synthesis/AzureCognitiveServicesTTS.ts +18 -15
- package/src/synthesis/CoquiServerTTS.ts +2 -2
- package/src/synthesis/ElevenlabsTTS.ts +1 -1
- package/src/synthesis/EspeakTTS.ts +0 -15
- package/src/synthesis/FliteTTS.ts +7 -8
- package/src/synthesis/GoogleCloudTTS.ts +3 -2
- package/src/synthesis/GoogleTranslateTTS.ts +5 -4
- package/src/synthesis/MicrosoftEdgeTTS.ts +47 -15
- package/src/synthesis/SapiTTS.ts +1 -1
- package/src/synthesis/StreamlabsPollyTTS.ts +1 -1
- package/src/synthesis/SvoxPicoTTS.ts +8 -8
- package/src/synthesis/VitsTTS.ts +2 -2
- package/src/tests/Test.ts +1 -60
- package/src/text-language-detection/FastTextLanguageDetection.ts +2 -2
- package/src/typings/Fillers.d.ts +2 -4
- package/src/typings/TypedArray.ts +5 -0
- package/src/utilities/BinaryArrayConversion.ts +51 -125
- package/src/utilities/BinaryUtilities.ts +138 -0
- package/src/utilities/Compression.ts +7 -6
- package/src/utilities/FileDownloader.ts +27 -52
- package/src/utilities/FileReader.ts +89 -0
- package/src/utilities/FileSystem.ts +213 -120
- package/src/utilities/FileWriter.ts +72 -0
- package/src/utilities/NpmUtilities.ts +23 -0
- package/src/utilities/PackageManager.ts +8 -5
- package/src/utilities/PathUtilities.ts +36 -7
- package/src/utilities/SignalChannel.ts +14 -7
- package/src/utilities/StringBuilder.ts +45 -0
- package/src/utilities/StringUtilities.ts +2 -0
- package/src/utilities/TarballMaker.ts +6 -7
- package/src/utilities/Timer.ts +4 -2
- package/src/utilities/Utilities.ts +79 -45
- package/src/utilities/VirtualFileReadStream.ts +5 -5
- package/src/utilities/WasmMemoryManager.ts +20 -6
- package/tsconfig.json +96 -0
- package/dist/utilities/DynamicUint8Array.d.ts +0 -9
- package/dist/utilities/DynamicUint8Array.js +0 -31
- package/dist/utilities/DynamicUint8Array.js.map +0 -1
- package/dist/utilities/LEB128.js.map +0 -1
- package/dist/utilities/LPVarInt.js.map +0 -1
- package/dist/utilities/Queue.js.map +0 -1
- package/dist/utilities/WindowedList.js.map +0 -1
- package/src/utilities/DynamicUint8Array.ts +0 -39
- /package/dist/{utilities → data-structures}/Queue.d.ts +0 -0
- /package/dist/{utilities → data-structures}/Queue.js +0 -0
- /package/dist/{utilities → data-structures}/WindowedList.d.ts +0 -0
- /package/dist/{utilities → data-structures}/WindowedList.js +0 -0
- /package/src/{utilities → data-structures}/Queue.ts +0 -0
- /package/src/{utilities → data-structures}/WindowedList.ts +0 -0
|
@@ -5,7 +5,7 @@ import { BitDepth, SampleFormat } from '../codecs/WaveCodec.js'
|
|
|
5
5
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
6
6
|
// Low level audio sample conversions
|
|
7
7
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
8
|
-
export function encodeToAudioBuffer(audioChannels: Float32Array[], targetBitDepth: BitDepth = 16, targetSampleFormat: SampleFormat = SampleFormat.PCM) {
|
|
8
|
+
export function encodeToAudioBuffer(audioChannels: Float32Array[], targetBitDepth: BitDepth = 16, targetSampleFormat: SampleFormat = SampleFormat.PCM): Uint8Array {
|
|
9
9
|
const interleavedChannels = interleaveChannels(audioChannels)
|
|
10
10
|
|
|
11
11
|
audioChannels = [] // Zero the array references to allow the GC to free up memory, if possible
|
|
@@ -32,13 +32,13 @@ export function encodeToAudioBuffer(audioChannels: Float32Array[], targetBitDept
|
|
|
32
32
|
}
|
|
33
33
|
} else if (targetSampleFormat === SampleFormat.Alaw) {
|
|
34
34
|
if (targetBitDepth === 8) {
|
|
35
|
-
return
|
|
35
|
+
return AlawMulaw.alaw.encode(float32ToInt16Pcm(interleavedChannels))
|
|
36
36
|
} else {
|
|
37
37
|
throw new Error(`Unsupported alaw bit depth: ${targetBitDepth}`)
|
|
38
38
|
}
|
|
39
39
|
} else if (targetSampleFormat === SampleFormat.Mulaw) {
|
|
40
40
|
if (targetBitDepth === 8) {
|
|
41
|
-
return
|
|
41
|
+
return AlawMulaw.mulaw.encode(float32ToInt16Pcm(interleavedChannels))
|
|
42
42
|
} else {
|
|
43
43
|
throw new Error(`Unsupported mulaw bit depth: ${targetBitDepth}`)
|
|
44
44
|
}
|
|
@@ -47,7 +47,7 @@ export function encodeToAudioBuffer(audioChannels: Float32Array[], targetBitDept
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
export function decodeToChannels(audioBuffer:
|
|
50
|
+
export function decodeToChannels(audioBuffer: Uint8Array, channelCount: number, sourceBitDepth: number, sourceSampleFormat: SampleFormat) {
|
|
51
51
|
let interleavedChannels: Float32Array
|
|
52
52
|
|
|
53
53
|
if (sourceSampleFormat === SampleFormat.PCM) {
|
|
@@ -86,29 +86,39 @@ export function decodeToChannels(audioBuffer: Buffer, channelCount: number, sour
|
|
|
86
86
|
throw new Error(`Unsupported audio format: ${sourceSampleFormat}`)
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
audioBuffer =
|
|
89
|
+
audioBuffer = new Uint8Array(0) // Zero the buffer reference to allow the GC to free up memory, if possible
|
|
90
90
|
|
|
91
91
|
return deInterleaveChannels(interleavedChannels, channelCount)
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
// Int8 PCM <-> Float32 conversion
|
|
95
95
|
export function int8PcmToFloat32(input: Int8Array) {
|
|
96
|
-
const
|
|
96
|
+
const sampleCount = input.length
|
|
97
97
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
const output = new Float32Array(sampleCount)
|
|
99
|
+
|
|
100
|
+
for (let i = 0; i < sampleCount; i++) {
|
|
101
|
+
output[i] = input[i] / 128
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
return output
|
|
104
105
|
}
|
|
105
106
|
|
|
106
107
|
export function float32ToInt8Pcm(input: Float32Array) {
|
|
107
|
-
const
|
|
108
|
+
const sampleCount = input.length
|
|
109
|
+
|
|
110
|
+
const output = new Int8Array(sampleCount)
|
|
111
|
+
|
|
112
|
+
for (let i = 0; i < sampleCount; i++) {
|
|
113
|
+
const int8Sample = input[i] * 128
|
|
108
114
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
115
|
+
if (int8Sample < -128) {
|
|
116
|
+
output[i] = -128
|
|
117
|
+
} else if (int8Sample > 127) {
|
|
118
|
+
output[i] = 127
|
|
119
|
+
} else {
|
|
120
|
+
output[i] = int8Sample
|
|
121
|
+
}
|
|
112
122
|
}
|
|
113
123
|
|
|
114
124
|
return output
|
|
@@ -116,22 +126,32 @@ export function float32ToInt8Pcm(input: Float32Array) {
|
|
|
116
126
|
|
|
117
127
|
// Int16 PCM <-> Float32 conversion
|
|
118
128
|
export function int16PcmToFloat32(input: Int16Array) {
|
|
119
|
-
const
|
|
129
|
+
const sampleCount = input.length
|
|
130
|
+
|
|
131
|
+
const output = new Float32Array(sampleCount)
|
|
120
132
|
|
|
121
|
-
for (let i = 0; i <
|
|
122
|
-
|
|
123
|
-
output[i] = sample < 0 ? sample / 32768 : sample / 32767
|
|
133
|
+
for (let i = 0; i < sampleCount; i++) {
|
|
134
|
+
output[i] = input[i] / 32768
|
|
124
135
|
}
|
|
125
136
|
|
|
126
137
|
return output
|
|
127
138
|
}
|
|
128
139
|
|
|
129
140
|
export function float32ToInt16Pcm(input: Float32Array) {
|
|
130
|
-
const
|
|
141
|
+
const sampleCount = input.length
|
|
142
|
+
|
|
143
|
+
const output = new Int16Array(sampleCount)
|
|
144
|
+
|
|
145
|
+
for (let i = 0; i < sampleCount; i++) {
|
|
146
|
+
const int16Sample = input[i] * 32768
|
|
131
147
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
148
|
+
if (int16Sample < -32768) {
|
|
149
|
+
output[i] = -32768
|
|
150
|
+
} else if (int16Sample > 32767) {
|
|
151
|
+
output[i] = 32767
|
|
152
|
+
} else {
|
|
153
|
+
output[i] = int16Sample
|
|
154
|
+
}
|
|
135
155
|
}
|
|
136
156
|
|
|
137
157
|
return output
|
|
@@ -139,22 +159,32 @@ export function float32ToInt16Pcm(input: Float32Array) {
|
|
|
139
159
|
|
|
140
160
|
// Int24 PCM <-> Float32 conversion (uses int32 for storage)
|
|
141
161
|
export function int24PcmToFloat32(input: Int32Array) {
|
|
142
|
-
const
|
|
162
|
+
const sampleCount = input.length
|
|
143
163
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
164
|
+
const output = new Float32Array(sampleCount)
|
|
165
|
+
|
|
166
|
+
for (let i = 0; i < sampleCount; i++) {
|
|
167
|
+
output[i] = input[i] / 8388608
|
|
147
168
|
}
|
|
148
169
|
|
|
149
170
|
return output
|
|
150
171
|
}
|
|
151
172
|
|
|
152
173
|
export function float32ToInt24Pcm(input: Float32Array) {
|
|
153
|
-
const
|
|
174
|
+
const sampleCount = input.length
|
|
154
175
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
176
|
+
const output = new Int32Array(sampleCount)
|
|
177
|
+
|
|
178
|
+
for (let i = 0; i < sampleCount; i++) {
|
|
179
|
+
const int24Sample = input[i] * 8388608
|
|
180
|
+
|
|
181
|
+
if (int24Sample < -8388608) {
|
|
182
|
+
output[i] = -8388608
|
|
183
|
+
} else if (int24Sample > 8388607) {
|
|
184
|
+
output[i] = 8388607
|
|
185
|
+
} else {
|
|
186
|
+
output[i] = int24Sample
|
|
187
|
+
}
|
|
158
188
|
}
|
|
159
189
|
|
|
160
190
|
return output
|
|
@@ -162,22 +192,32 @@ export function float32ToInt24Pcm(input: Float32Array) {
|
|
|
162
192
|
|
|
163
193
|
// Int32 PCM <-> Float32 conversion
|
|
164
194
|
export function int32PcmToFloat32(input: Int32Array) {
|
|
165
|
-
const
|
|
195
|
+
const sampleCount = input.length
|
|
196
|
+
|
|
197
|
+
const output = new Float32Array(sampleCount)
|
|
166
198
|
|
|
167
|
-
for (let i = 0; i <
|
|
168
|
-
|
|
169
|
-
output[i] = sample < 0 ? sample / 2147483648 : sample / 2147483647
|
|
199
|
+
for (let i = 0; i < sampleCount; i++) {
|
|
200
|
+
output[i] = input[i] / 2147483648
|
|
170
201
|
}
|
|
171
202
|
|
|
172
203
|
return output
|
|
173
204
|
}
|
|
174
205
|
|
|
175
206
|
export function float32ToInt32Pcm(input: Float32Array) {
|
|
176
|
-
const
|
|
207
|
+
const sampleCount = input.length
|
|
208
|
+
|
|
209
|
+
const output = new Int32Array(sampleCount)
|
|
177
210
|
|
|
178
|
-
for (let i = 0; i <
|
|
179
|
-
const
|
|
180
|
-
|
|
211
|
+
for (let i = 0; i < sampleCount; i++) {
|
|
212
|
+
const int32Sample = input[i] * 2147483648
|
|
213
|
+
|
|
214
|
+
if (int32Sample < -2147483648) {
|
|
215
|
+
output[i] = -2147483648
|
|
216
|
+
} else if (int32Sample > 2147483647) {
|
|
217
|
+
output[i] = 2147483647
|
|
218
|
+
} else {
|
|
219
|
+
output[i] = int32Sample
|
|
220
|
+
}
|
|
181
221
|
}
|
|
182
222
|
|
|
183
223
|
return output
|
|
@@ -202,10 +242,9 @@ export function interleaveChannels(channels: Float32Array[]) {
|
|
|
202
242
|
|
|
203
243
|
let writeIndex = 0
|
|
204
244
|
|
|
205
|
-
for (let
|
|
206
|
-
for (let
|
|
207
|
-
result[writeIndex] = channels[
|
|
208
|
-
writeIndex += 1
|
|
245
|
+
for (let sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++) {
|
|
246
|
+
for (let channelIndex = 0; channelIndex < channelCount; channelIndex++) {
|
|
247
|
+
result[writeIndex++] = channels[channelIndex][sampleIndex]
|
|
209
248
|
}
|
|
210
249
|
}
|
|
211
250
|
|
|
@@ -226,6 +265,7 @@ export function deInterleaveChannels(interleavedChannels: Float32Array, channelC
|
|
|
226
265
|
}
|
|
227
266
|
|
|
228
267
|
const sampleCount = interleavedChannels.length / channelCount
|
|
268
|
+
|
|
229
269
|
const channels: Float32Array[] = []
|
|
230
270
|
|
|
231
271
|
for (let i = 0; i < channelCount; i++) {
|
|
@@ -234,10 +274,9 @@ export function deInterleaveChannels(interleavedChannels: Float32Array, channelC
|
|
|
234
274
|
|
|
235
275
|
let readIndex = 0
|
|
236
276
|
|
|
237
|
-
for (let
|
|
238
|
-
for (let
|
|
239
|
-
channels[
|
|
240
|
-
readIndex += 1
|
|
277
|
+
for (let sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++) {
|
|
278
|
+
for (let channelIndex = 0; channelIndex < channelCount; channelIndex++) {
|
|
279
|
+
channels[channelIndex][sampleIndex] = interleavedChannels[readIndex++]
|
|
241
280
|
}
|
|
242
281
|
}
|
|
243
282
|
|
package/src/audio/AudioPlayer.ts
CHANGED
|
@@ -2,37 +2,36 @@ import { parentPort } from 'node:worker_threads'
|
|
|
2
2
|
|
|
3
3
|
import { ChildProcessWithoutNullStreams, spawn } from 'child_process'
|
|
4
4
|
|
|
5
|
-
import { RawAudio, encodeRawAudioToWave, fadeAudioInOut, getRawAudioDuration, sliceRawAudioByTime } from './AudioUtilities.js'
|
|
6
|
-
import * as AudioBufferConversion from './AudioBufferConversion.js'
|
|
5
|
+
import { RawAudio, encodeRawAudioToWave, fadeAudioInOut, getRawAudioDuration, sliceAudioChannels, sliceRawAudioByTime } from './AudioUtilities.js'
|
|
7
6
|
import * as FFMpegTranscoder from '../codecs/FFMpegTranscoder.js'
|
|
8
7
|
|
|
9
8
|
import { Timer } from '../utilities/Timer.js'
|
|
10
|
-
import { getRandomHexString, waitTimeout, writeToStderr } from '../utilities/Utilities.js'
|
|
11
|
-
import { encodeToAudioBuffer } from './AudioBufferConversion.js'
|
|
9
|
+
import { clip, getRandomHexString, waitTimeout, writeToStderr } from '../utilities/Utilities.js'
|
|
10
|
+
import { encodeToAudioBuffer, float32ToInt16Pcm, interleaveChannels } from './AudioBufferConversion.js'
|
|
12
11
|
import { OpenPromise } from '../utilities/OpenPromise.js'
|
|
13
12
|
import { Timeline, addWordTextOffsetsToTimeline } from '../utilities/Timeline.js'
|
|
14
|
-
import { readAndParseJsonFile,
|
|
13
|
+
import { readAndParseJsonFile, readFileAsUtf8, remove, writeFile } from '../utilities/FileSystem.js'
|
|
15
14
|
import { tryResolvingSoxPath } from './SoxPath.js'
|
|
16
15
|
import { SignalChannel } from '../utilities/SignalChannel.js'
|
|
17
16
|
import { deepClone } from '../utilities/ObjectUtilities.js'
|
|
18
17
|
import { appName } from '../api/Common.js'
|
|
19
|
-
import
|
|
20
|
-
import {
|
|
18
|
+
import { getAppTempDir, joinPath } from '../utilities/PathUtilities.js'
|
|
19
|
+
import { type AudioOutput } from '@echogarden/audio-io'
|
|
21
20
|
|
|
22
|
-
export async function playAudioFileWithTimelineFile(audioFilename: string, timelineFileName: string, transcriptFileName?: string) {
|
|
21
|
+
export async function playAudioFileWithTimelineFile(audioFilename: string, timelineFileName: string, transcriptFileName?: string, player?: AudioPlayerID) {
|
|
23
22
|
const rawAudio = await FFMpegTranscoder.decodeToChannels(audioFilename, 48000, 1)
|
|
24
23
|
|
|
25
24
|
const timeline = await readAndParseJsonFile(timelineFileName)
|
|
26
25
|
|
|
27
26
|
let transcript: string | undefined
|
|
28
27
|
if (transcriptFileName) {
|
|
29
|
-
transcript = await
|
|
28
|
+
transcript = await readFileAsUtf8(transcriptFileName)
|
|
30
29
|
}
|
|
31
30
|
|
|
32
|
-
await playAudioWithWordTimeline(rawAudio, timeline, transcript)
|
|
31
|
+
await playAudioWithWordTimeline(rawAudio, timeline, transcript, player)
|
|
33
32
|
}
|
|
34
33
|
|
|
35
|
-
export async function playAudioWithWordTimeline(rawAudio: RawAudio, wordTimeline: Timeline, transcript?: string) {
|
|
34
|
+
export async function playAudioWithWordTimeline(rawAudio: RawAudio, wordTimeline: Timeline, transcript?: string, player?: AudioPlayerID) {
|
|
36
35
|
if (!transcript) {
|
|
37
36
|
transcript = wordTimeline.map(entry => entry.text).join(' ')
|
|
38
37
|
}
|
|
@@ -82,77 +81,178 @@ export async function playAudioWithWordTimeline(rawAudio: RawAudio, wordTimeline
|
|
|
82
81
|
const keypressListenerStartTimestamp = Date.now()
|
|
83
82
|
|
|
84
83
|
function keypressHandler(message: any) {
|
|
85
|
-
if (message.name
|
|
86
|
-
message.key.name
|
|
87
|
-
message.timestamp >= keypressListenerStartTimestamp) {
|
|
84
|
+
if (message.name === 'keypress') {
|
|
85
|
+
//console.log(`Keypress: ${message.key.name}`)
|
|
88
86
|
|
|
89
|
-
|
|
87
|
+
if (message.timestamp < keypressListenerStartTimestamp) {
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const keyName = message.key.name
|
|
92
|
+
|
|
93
|
+
if (keyName === 'return') {
|
|
94
|
+
signalChannel.send('abort')
|
|
95
|
+
} else if (keyName === 'left') {
|
|
96
|
+
signalChannel.send('skip', -1)
|
|
97
|
+
} else if (keyName === 'right') {
|
|
98
|
+
signalChannel.send('skip', 1)
|
|
99
|
+
|
|
100
|
+
if (false) {
|
|
101
|
+
const nextTimelineEntryIndex = timelineEntryIndex
|
|
102
|
+
|
|
103
|
+
if (nextTimelineEntryIndex < wordTimeline.length) {
|
|
104
|
+
//signalChannel.send('skip', 5)
|
|
105
|
+
const targetTime = wordTimeline[nextTimelineEntryIndex].startTime
|
|
106
|
+
|
|
107
|
+
signalChannel.send('seek', targetTime)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} else if (keyName === 'space') {
|
|
111
|
+
signalChannel.send('togglePause')
|
|
112
|
+
}
|
|
90
113
|
}
|
|
91
114
|
}
|
|
92
115
|
|
|
93
116
|
parentPort?.on('message', keypressHandler)
|
|
94
117
|
|
|
95
|
-
await playAudioSamples(rawAudio, onTimePosition, signalChannel)
|
|
118
|
+
await playAudioSamples(rawAudio, onTimePosition, signalChannel, player)
|
|
96
119
|
|
|
97
120
|
parentPort?.off('message', keypressHandler)
|
|
98
121
|
|
|
99
122
|
writeToStderr('\n')
|
|
100
123
|
}
|
|
101
124
|
|
|
102
|
-
export async function
|
|
103
|
-
|
|
104
|
-
let phoneIndex = 0
|
|
125
|
+
export async function playAudioSamplesWithKeyboardControls(rawAudio: RawAudio, player?: AudioPlayerID) {
|
|
126
|
+
const signalChannel = new SignalChannel()
|
|
105
127
|
|
|
106
|
-
|
|
107
|
-
for (; wordIndex < timeline.length; wordIndex++) {
|
|
108
|
-
const wordEntry = timeline[wordIndex]
|
|
109
|
-
const phoneTimeline = wordEntry.timeline!
|
|
128
|
+
const keypressListenerStartTimestamp = Date.now()
|
|
110
129
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
130
|
+
function keypressHandler(message: any) {
|
|
131
|
+
if (message.name === 'keypress') {
|
|
132
|
+
//console.log(`Keypress: ${message.key.name}`)
|
|
114
133
|
|
|
115
|
-
|
|
116
|
-
|
|
134
|
+
if (message.timestamp < keypressListenerStartTimestamp) {
|
|
135
|
+
return
|
|
136
|
+
}
|
|
117
137
|
|
|
118
|
-
|
|
119
|
-
return
|
|
120
|
-
}
|
|
138
|
+
const keyName = message.key.name
|
|
121
139
|
|
|
122
|
-
|
|
140
|
+
if (keyName === 'return') {
|
|
141
|
+
signalChannel.send('abort')
|
|
142
|
+
} else if (keyName === 'left') {
|
|
143
|
+
signalChannel.send('skip', -1)
|
|
144
|
+
} else if (keyName === 'right') {
|
|
145
|
+
signalChannel.send('skip', 1)
|
|
146
|
+
} else if (keyName === 'space') {
|
|
147
|
+
signalChannel.send('togglePause')
|
|
123
148
|
}
|
|
124
|
-
|
|
125
|
-
writeToStderr(`| `)
|
|
126
|
-
phoneIndex = 0
|
|
127
149
|
}
|
|
128
150
|
}
|
|
129
151
|
|
|
130
|
-
|
|
152
|
+
parentPort?.on('message', keypressHandler)
|
|
131
153
|
|
|
132
|
-
await playAudioSamples(rawAudio,
|
|
154
|
+
await playAudioSamples(rawAudio, undefined, signalChannel, player)
|
|
133
155
|
|
|
134
|
-
|
|
156
|
+
parentPort?.off('message', keypressHandler)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function playAudioSamples(rawAudio: RawAudio, onTimePosition?: (timePosition: number) => void, signalChannel?: SignalChannel, player?: AudioPlayerID) {
|
|
160
|
+
if (!player) {
|
|
161
|
+
player = 'audio-io'
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (player === 'audio-io') {
|
|
165
|
+
return playAudioSamples_AudioIO(rawAudio, onTimePosition, signalChannel)
|
|
166
|
+
} else if (player === 'sox') {
|
|
167
|
+
return playAudioSamples_Sox(rawAudio, onTimePosition, signalChannel)
|
|
168
|
+
} else {
|
|
169
|
+
throw new Error(`Unsupported audio player ID: ${player}`)
|
|
170
|
+
}
|
|
135
171
|
}
|
|
136
172
|
|
|
173
|
+
export async function playAudioSamples_AudioIO(rawAudio: RawAudio, onTimePosition?: (timePosition: number) => void, signalChannel?: SignalChannel) {
|
|
174
|
+
const openPromise = new OpenPromise()
|
|
175
|
+
|
|
176
|
+
const sampleRate = rawAudio.sampleRate
|
|
177
|
+
const channelCount = rawAudio.audioChannels.length
|
|
178
|
+
const bufferDuration = 100.0
|
|
179
|
+
const audioFrameCount = rawAudio.audioChannels[0].length
|
|
180
|
+
const audioDuration = getRawAudioDuration(rawAudio)
|
|
181
|
+
|
|
182
|
+
const { createAudioOutput } = await import('@echogarden/audio-io')
|
|
183
|
+
|
|
184
|
+
let frameOffset = 0
|
|
185
|
+
let audioOutput: AudioOutput
|
|
186
|
+
let abortRequested = false
|
|
187
|
+
let ended = false
|
|
188
|
+
|
|
189
|
+
let isPaused = false
|
|
190
|
+
|
|
191
|
+
// Define an audio output handler function
|
|
192
|
+
async function audioOutputHandler(outputBuffer: Int16Array) {
|
|
193
|
+
if (ended) {
|
|
194
|
+
return
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (onTimePosition) {
|
|
198
|
+
const audioTime = Math.min(frameOffset / sampleRate, audioDuration)
|
|
199
|
+
|
|
200
|
+
onTimePosition(audioTime)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (isPaused) {
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const chunkFrameCount = outputBuffer.length / channelCount
|
|
208
|
+
|
|
209
|
+
const floatAudioChunk = sliceAudioChannels(rawAudio.audioChannels, frameOffset, frameOffset + chunkFrameCount)
|
|
210
|
+
const interleavedFloatAudioChunk = interleaveChannels(floatAudioChunk)
|
|
211
|
+
const int16AudioChunk = float32ToInt16Pcm(interleavedFloatAudioChunk)
|
|
137
212
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
213
|
+
outputBuffer.set(int16AudioChunk)
|
|
214
|
+
|
|
215
|
+
frameOffset += chunkFrameCount
|
|
216
|
+
|
|
217
|
+
if (abortRequested || int16AudioChunk.length < outputBuffer.length) {
|
|
218
|
+
ended = true
|
|
219
|
+
|
|
220
|
+
await audioOutput.dispose()
|
|
221
|
+
openPromise.resolve()
|
|
222
|
+
}
|
|
141
223
|
}
|
|
142
224
|
|
|
143
|
-
|
|
144
|
-
|
|
225
|
+
if (signalChannel) {
|
|
226
|
+
signalChannel.on('abort', () => {
|
|
227
|
+
abortRequested = true
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
signalChannel.on('skip', (durationToSkip: number) => {
|
|
231
|
+
frameOffset += durationToSkip * sampleRate
|
|
232
|
+
frameOffset = Math.floor(frameOffset)
|
|
233
|
+
frameOffset = clip(frameOffset, 0, audioFrameCount)
|
|
234
|
+
})
|
|
145
235
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
236
|
+
signalChannel.on('seek', (timeToSeekTo: number) => {
|
|
237
|
+
frameOffset = Math.floor(timeToSeekTo * sampleRate)
|
|
238
|
+
frameOffset = clip(frameOffset, 0, audioFrameCount)
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
signalChannel.on('togglePause', () => {
|
|
242
|
+
isPaused = !isPaused
|
|
243
|
+
})
|
|
150
244
|
}
|
|
151
245
|
|
|
152
|
-
|
|
246
|
+
audioOutput = await createAudioOutput({
|
|
247
|
+
sampleRate, // Sample rate in Hz, should be an integer like 44100, 22050, 8000
|
|
248
|
+
channelCount, // Channel count, likely 1 (mono), or 2 (stereo)
|
|
249
|
+
bufferDuration, // Target buffer duration, in milliseconds. Defaults to 100.0
|
|
250
|
+
}, audioOutputHandler)
|
|
251
|
+
|
|
252
|
+
return openPromise.promise
|
|
153
253
|
}
|
|
154
254
|
|
|
155
|
-
export function
|
|
255
|
+
export function playAudioSamples_Sox(rawAudio: RawAudio, onTimePosition?: (timePosition: number) => void, signalChannel?: SignalChannel, microFadeInOut = true) {
|
|
156
256
|
return new Promise<void>(async (resolve, reject) => {
|
|
157
257
|
if (microFadeInOut) {
|
|
158
258
|
rawAudio = fadeAudioInOut(rawAudio, 0.0025)
|
|
@@ -180,7 +280,7 @@ export function playAudioSamples(rawAudio: RawAudio, onTimePosition?: (timePosit
|
|
|
180
280
|
}
|
|
181
281
|
|
|
182
282
|
let tempFilePath: string | undefined
|
|
183
|
-
let audioBuffer:
|
|
283
|
+
let audioBuffer: Uint8Array | undefined
|
|
184
284
|
|
|
185
285
|
async function cleanup() {
|
|
186
286
|
if (tempFilePath) {
|
|
@@ -199,7 +299,7 @@ export function playAudioSamples(rawAudio: RawAudio, onTimePosition?: (timePosit
|
|
|
199
299
|
{}
|
|
200
300
|
)
|
|
201
301
|
} else {
|
|
202
|
-
tempFilePath =
|
|
302
|
+
tempFilePath = joinPath(getAppTempDir(appName), `${getRandomHexString(16)}.wav`)
|
|
203
303
|
const waveFileBuffer = encodeRawAudioToWave(rawAudio)
|
|
204
304
|
await writeFile(tempFilePath, waveFileBuffer)
|
|
205
305
|
|
|
@@ -282,7 +382,7 @@ export function playAudioSamples_Speaker(rawAudio: RawAudio, onTimePosition?: (t
|
|
|
282
382
|
}
|
|
283
383
|
|
|
284
384
|
const channelCount = rawAudio.audioChannels.length
|
|
285
|
-
let audioData =
|
|
385
|
+
let audioData = encodeToAudioBuffer(rawAudio.audioChannels)
|
|
286
386
|
|
|
287
387
|
const { default: Speaker } = await import('speaker')
|
|
288
388
|
|
|
@@ -315,7 +415,7 @@ export function playAudioSamples_Speaker(rawAudio: RawAudio, onTimePosition?: (t
|
|
|
315
415
|
mpg123AudioBufferSize = bytesPerSecond * mpg123AudioBufferDuration
|
|
316
416
|
}
|
|
317
417
|
|
|
318
|
-
audioData =
|
|
418
|
+
audioData = concatUint8Arrays([audioData, new Uint8Array(mpg123AudioBufferSize)])
|
|
319
419
|
|
|
320
420
|
const maxChunkSize = mpg123AudioBufferSize
|
|
321
421
|
|
|
@@ -363,3 +463,5 @@ export const charactersToWriteAhead = [
|
|
|
363
463
|
')', ']', '}', `"`, `'`, '”', '’',
|
|
364
464
|
'-', '—', '»', '،', '؟'
|
|
365
465
|
]
|
|
466
|
+
|
|
467
|
+
export type AudioPlayerID = 'audio-io' | 'sox'
|
|
@@ -11,7 +11,7 @@ export function encodeRawAudioToWave(rawAudio: RawAudio, bitDepth: BitDepth = 16
|
|
|
11
11
|
return encodeWave(rawAudio, bitDepth, sampleFormat, speakerPositionMask)
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export function decodeWaveToRawAudio(waveFileBuffer:
|
|
14
|
+
export function decodeWaveToRawAudio(waveFileBuffer: Uint8Array, ignoreTruncatedChunks = true, ignoreOverflowingDataChunks = true) {
|
|
15
15
|
return decodeWave(waveFileBuffer, ignoreTruncatedChunks, ignoreOverflowingDataChunks)
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -401,33 +401,25 @@ export function getRawAudioDuration(rawAudio: RawAudio) {
|
|
|
401
401
|
export async function ensureRawAudio(input: AudioSourceParam, outSampleRate?: number, outChannelCount?: number) {
|
|
402
402
|
let inputAsRawAudio: RawAudio = input as RawAudio
|
|
403
403
|
|
|
404
|
-
if (
|
|
404
|
+
if (isRawAudio(input)) {
|
|
405
405
|
const inputAudioChannelCount = inputAsRawAudio.audioChannels.length
|
|
406
406
|
|
|
407
407
|
if (outChannelCount == 1 && inputAudioChannelCount > 1) {
|
|
408
408
|
inputAsRawAudio = downmixToMono(inputAsRawAudio)
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
if (outChannelCount == 2 && inputAudioChannelCount == 1) {
|
|
409
|
+
} else if (outChannelCount == 2 && inputAudioChannelCount == 1) {
|
|
412
410
|
inputAsRawAudio = cloneRawAudio(inputAsRawAudio)
|
|
413
411
|
inputAsRawAudio.audioChannels.push(inputAsRawAudio.audioChannels[0].slice())
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
if (outChannelCount != null && outChannelCount > 2 && outChannelCount != inputAudioChannelCount) {
|
|
412
|
+
} else if (outChannelCount != null && outChannelCount > 2 && outChannelCount != inputAudioChannelCount) {
|
|
417
413
|
throw new Error(`Can't convert ${inputAudioChannelCount} channels to ${outChannelCount} channels. Channel conversion of raw audio currently only supports mono and stereo inputs.`)
|
|
418
414
|
}
|
|
419
415
|
|
|
420
|
-
if (outSampleRate && inputAsRawAudio.sampleRate
|
|
416
|
+
if (outSampleRate && inputAsRawAudio.sampleRate !== outSampleRate) {
|
|
421
417
|
inputAsRawAudio = await resampleAudioSpeex(inputAsRawAudio, outSampleRate)
|
|
422
418
|
}
|
|
423
419
|
} else if (typeof input == 'string' || input instanceof Uint8Array) {
|
|
424
|
-
|
|
425
|
-
input = Buffer.from(input)
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
const inputAsStringOrBuffer = input as string | Buffer
|
|
420
|
+
const inputAsStringOrUint8Array = input as string | Uint8Array
|
|
429
421
|
|
|
430
|
-
inputAsRawAudio = await FFMpegTranscoder.decodeToChannels(
|
|
422
|
+
inputAsRawAudio = await FFMpegTranscoder.decodeToChannels(inputAsStringOrUint8Array, outSampleRate, outChannelCount)
|
|
431
423
|
} else {
|
|
432
424
|
throw new Error('Received an invalid input audio data type.')
|
|
433
425
|
}
|
|
@@ -436,21 +428,45 @@ export async function ensureRawAudio(input: AudioSourceParam, outSampleRate?: nu
|
|
|
436
428
|
}
|
|
437
429
|
|
|
438
430
|
export function subtractAudio(audio1: RawAudio, audio2: RawAudio) {
|
|
439
|
-
|
|
431
|
+
if (audio1.sampleRate !== audio2.sampleRate) {
|
|
432
|
+
throw new Error(`Audio sequences have different sample rates`)
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (audio1.audioChannels.length !== audio2.audioChannels.length) {
|
|
436
|
+
throw new Error(`Audio sequences have different channel counts`)
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const sampleRate = audio1.sampleRate
|
|
440
|
+
const channelCount = audio1.audioChannels.length
|
|
441
|
+
const sampleCount = Math.min(audio1.audioChannels[0].length, audio2.audioChannels[0].length)
|
|
442
|
+
|
|
443
|
+
const subtractedAudioChannels: Float32Array[] = []
|
|
444
|
+
|
|
445
|
+
for (let channelIndex = 0; channelIndex < channelCount; channelIndex++) {
|
|
446
|
+
const subtractedSamples = new Float32Array(sampleCount)
|
|
440
447
|
|
|
441
|
-
|
|
448
|
+
const samples1 = audio1.audioChannels[channelIndex]
|
|
449
|
+
const samples2 = audio2.audioChannels[channelIndex]
|
|
442
450
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
subtractedAudioChannels[channelIndex][i] = audio1.audioChannels[channelIndex][i] - audio2.audioChannels[channelIndex][i]
|
|
451
|
+
for (let sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++) {
|
|
452
|
+
subtractedSamples[sampleIndex] = samples1[sampleIndex] - samples2[sampleIndex]
|
|
446
453
|
}
|
|
454
|
+
|
|
455
|
+
subtractedAudioChannels.push(subtractedSamples)
|
|
447
456
|
}
|
|
448
457
|
|
|
449
|
-
const subtractedRawAudio: RawAudio = {
|
|
458
|
+
const subtractedRawAudio: RawAudio = {
|
|
459
|
+
audioChannels: subtractedAudioChannels,
|
|
460
|
+
sampleRate: sampleRate
|
|
461
|
+
}
|
|
450
462
|
|
|
451
463
|
return subtractedRawAudio
|
|
452
464
|
}
|
|
453
465
|
|
|
466
|
+
export function isRawAudio(obj: any): obj is RawAudio {
|
|
467
|
+
return typeof obj === 'object' && typeof obj.sampleRate === 'number' && Array.isArray(obj.audioChannels)
|
|
468
|
+
}
|
|
469
|
+
|
|
454
470
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
455
471
|
// Unit conversion
|
|
456
472
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
@@ -492,4 +508,4 @@ export type AudioEncoding = {
|
|
|
492
508
|
bitrate?: number
|
|
493
509
|
}
|
|
494
510
|
|
|
495
|
-
export type AudioSourceParam = string |
|
|
511
|
+
export type AudioSourceParam = string | Uint8Array | RawAudio
|