nodejs-audio-visualizer 4.0.0 → 4.1.1
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 +9 -8
- package/dist/beats.d.ts +19 -0
- package/dist/beats.js +44 -0
- package/dist/beats.js.map +1 -0
- package/dist/bpmEncoder.d.ts +4 -0
- package/dist/bpmEncoder.js +65 -43
- package/dist/bpmEncoder.js.map +1 -1
- package/dist/config.d.ts +7 -1
- package/dist/config.js +8 -1
- package/dist/config.js.map +1 -1
- package/dist/image.d.ts +2 -1
- package/dist/image.js +42 -5
- package/dist/image.js.map +1 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +217 -50
- package/dist/index.js.map +1 -1
- package/dist/video.d.ts +41 -1
- package/dist/video.js +244 -0
- package/dist/video.js.map +1 -1
- package/package.json +2 -2
- package/src/beats.ts +68 -0
- package/src/bpmEncoder.ts +57 -31
- package/src/config.ts +10 -1
- package/src/image.ts +46 -4
- package/src/index.ts +262 -34
- package/src/video.ts +302 -1
package/src/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getAudioFilePath,
|
|
3
3
|
getBackgroundImagePath,
|
|
4
|
+
getBackgroundVideoPath,
|
|
4
5
|
getOutVideoPath,
|
|
5
6
|
getFPS,
|
|
6
7
|
getSpectrumBusMargin,
|
|
@@ -13,6 +14,7 @@ import {
|
|
|
13
14
|
getFfmpeg_cfr,
|
|
14
15
|
getFfmpeg_preset,
|
|
15
16
|
getFrame_processing_delay,
|
|
17
|
+
getOutputResolution,
|
|
16
18
|
rotationAliasValues,
|
|
17
19
|
getSpectrumRotation,
|
|
18
20
|
getSpectrumEffect,
|
|
@@ -25,12 +27,14 @@ import {
|
|
|
25
27
|
getPolarEffect,
|
|
26
28
|
getPolarColor,
|
|
27
29
|
getPolarOpacityParsed,
|
|
30
|
+
getAutoEditVideo,
|
|
28
31
|
} from './config';
|
|
29
32
|
import { createAudioBuffer, bufferToUInt8, createSpectrumsProcessor } from './audio';
|
|
30
|
-
import { parseImage, getImageColor, invertColor, Color, convertToBmp, createSpectrumVisualizerFrameGenerator, createPolarVisualizerFrameGenerator, CreatePolarVisualizerFrameProps, CreateVisualizerFrameProps, CommonVisualizerFrameProps } from './image';
|
|
31
|
-
import { spawnFfmpegVideoWriter, getProgress, calculateProgress, waitDrain } from './video';
|
|
32
|
-
import { createBpmEncoder, EncodedBmp } from './bpmEncoder';
|
|
33
|
-
import {
|
|
33
|
+
import { parseImage, getImageColor, getVideoFrameColor, invertColor, Color, convertToBmp, createSpectrumVisualizerFrameGenerator, createPolarVisualizerFrameGenerator, CreatePolarVisualizerFrameProps, CreateVisualizerFrameProps, CommonVisualizerFrameProps } from './image';
|
|
34
|
+
import { spawnFfmpegVideoWriter, getProgress, calculateProgress, waitDrain, getVideoInfo, spawnVideoFrameReader, readVideoFrame, detectSceneChanges, buildBeatSyncedSegments, writeConcatFile, spawnConcatVideoFrameReader, cleanupConcatFile } from './video';
|
|
35
|
+
import { createBpmEncoder, createBgrFrameEncoder, EncodedBmp } from './bpmEncoder';
|
|
36
|
+
import { createBeatDetector } from './beats';
|
|
37
|
+
export { BeatInfo, BeatDetectorOptions } from './beats';
|
|
34
38
|
|
|
35
39
|
export const PCM_FORMAT = {
|
|
36
40
|
bit: 8,
|
|
@@ -44,12 +48,20 @@ export interface Config {
|
|
|
44
48
|
audio: {
|
|
45
49
|
path: string
|
|
46
50
|
};
|
|
47
|
-
image
|
|
51
|
+
image?: {
|
|
48
52
|
path: string;
|
|
49
53
|
};
|
|
54
|
+
video?: {
|
|
55
|
+
path: string;
|
|
56
|
+
autoEdit?: boolean;
|
|
57
|
+
};
|
|
50
58
|
outVideo: {
|
|
51
59
|
path: string;
|
|
52
60
|
fps?: number;
|
|
61
|
+
resolution?: {
|
|
62
|
+
width: number;
|
|
63
|
+
height: number;
|
|
64
|
+
};
|
|
53
65
|
spectrum?: {
|
|
54
66
|
width?: SpectrumSizeValue;
|
|
55
67
|
height?: SpectrumSizeValue;
|
|
@@ -95,17 +107,19 @@ const sleep = (timeout: number) =>
|
|
|
95
107
|
|
|
96
108
|
const createVisualizerFrameGenerator = (
|
|
97
109
|
config: Config,
|
|
98
|
-
|
|
110
|
+
backgroundWidth: number,
|
|
111
|
+
backgroundHeight: number,
|
|
112
|
+
defaultColor: Color,
|
|
99
113
|
spectrumBusMargin: number
|
|
100
114
|
): (params: CommonVisualizerFrameProps) => EncodedBmp => {
|
|
101
115
|
if (!config.outVideo.spectrum) {
|
|
102
116
|
const createPolarVisualizerFrame = createPolarVisualizerFrameGenerator();
|
|
103
|
-
const polarX = getPolarXAbsolute(config,
|
|
104
|
-
const polarY = getPolarYAbsolute(config,
|
|
117
|
+
const polarX = getPolarXAbsolute(config, backgroundWidth);
|
|
118
|
+
const polarY = getPolarYAbsolute(config, backgroundHeight);
|
|
105
119
|
const polarInnerRadius = getPolarInnerRadius(config);
|
|
106
120
|
const polarMaxBarLength = getPolarMaxBarLength(config);
|
|
107
121
|
const polarBarWidth = getPolarBarWidth(config);
|
|
108
|
-
const polarColor = getPolarColor(config) || invertColor(
|
|
122
|
+
const polarColor = getPolarColor(config) || invertColor(defaultColor);
|
|
109
123
|
const polarEffect = getPolarEffect(config);
|
|
110
124
|
const polarOpacity = getPolarOpacityParsed(config);
|
|
111
125
|
|
|
@@ -123,18 +137,18 @@ const createVisualizerFrameGenerator = (
|
|
|
123
137
|
});
|
|
124
138
|
};
|
|
125
139
|
}
|
|
126
|
-
const
|
|
127
|
-
const spectrumWidth = getSpectrumWidthAbsolute(config,
|
|
128
|
-
const spectrumHeight = getSpectrumHeightAbsolute(config,
|
|
129
|
-
const spectrumX = getSpectrumXAbsolute(config, spectrumWidth,
|
|
130
|
-
const spectrumY = getSpectrumYAbsolute(config, spectrumHeight,
|
|
140
|
+
const createSpectrumVisualizerFrame = createSpectrumVisualizerFrameGenerator();
|
|
141
|
+
const spectrumWidth = getSpectrumWidthAbsolute(config, backgroundWidth);
|
|
142
|
+
const spectrumHeight = getSpectrumHeightAbsolute(config, backgroundHeight);
|
|
143
|
+
const spectrumX = getSpectrumXAbsolute(config, spectrumWidth, backgroundWidth);
|
|
144
|
+
const spectrumY = getSpectrumYAbsolute(config, spectrumHeight, backgroundHeight);
|
|
131
145
|
const spectrumRotation = getSpectrumRotation(config);
|
|
132
|
-
const spectrumColor = getSpectrumColor(config) || invertColor(
|
|
146
|
+
const spectrumColor = getSpectrumColor(config) || invertColor(defaultColor);
|
|
133
147
|
const spectrumEffect = getSpectrumEffect(config);
|
|
134
148
|
const spectrumOpacity = getSpectrumOpacityParsed(config);
|
|
135
149
|
|
|
136
150
|
return (params: CommonVisualizerFrameProps) => {
|
|
137
|
-
return
|
|
151
|
+
return createSpectrumVisualizerFrame({
|
|
138
152
|
...params,
|
|
139
153
|
size: { width: spectrumWidth, height: spectrumHeight },
|
|
140
154
|
position: { x: spectrumX, y: spectrumY },
|
|
@@ -147,19 +161,201 @@ const createVisualizerFrameGenerator = (
|
|
|
147
161
|
};
|
|
148
162
|
};
|
|
149
163
|
|
|
164
|
+
interface PreProcessedAudio {
|
|
165
|
+
spectrums: number[][];
|
|
166
|
+
beatFrameIndices: number[];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const PREPROCESS_YIELD_EVERY = 256;
|
|
170
|
+
|
|
171
|
+
const preProcessAudio = async (
|
|
172
|
+
audioBuffer: Buffer,
|
|
173
|
+
sampleRate: number,
|
|
174
|
+
fps: number,
|
|
175
|
+
framesCount: number,
|
|
176
|
+
): Promise<PreProcessedAudio> => {
|
|
177
|
+
const audioDataStep = Math.trunc(audioBuffer.length / framesCount);
|
|
178
|
+
const processingBuffer = new Float32Array(PROCESSING_BUFFER_SIZE).fill(0);
|
|
179
|
+
const skipFramesCount = fps < 45 ? 1 : 2;
|
|
180
|
+
const processSpectrum = createSpectrumsProcessor(sampleRate, skipFramesCount);
|
|
181
|
+
const detectBeat = createBeatDetector(fps);
|
|
182
|
+
|
|
183
|
+
const spectrums: number[][] = [];
|
|
184
|
+
const beatFrameIndices: number[] = [];
|
|
185
|
+
|
|
186
|
+
for (let i = 0; i < framesCount; i++) {
|
|
187
|
+
const currentFrameData = PCM_FORMAT.parseFunction(audioBuffer, i * audioDataStep, i * audioDataStep + audioDataStep);
|
|
188
|
+
processingBuffer.copyWithin(0, currentFrameData.length);
|
|
189
|
+
processingBuffer.set(currentFrameData, PROCESSING_BUFFER_SIZE - currentFrameData.length);
|
|
190
|
+
|
|
191
|
+
const audioDataParser = () => Array.from(processingBuffer);
|
|
192
|
+
const spectrum = processSpectrum(audioDataParser);
|
|
193
|
+
const beat = detectBeat(spectrum);
|
|
194
|
+
|
|
195
|
+
spectrums.push(spectrum);
|
|
196
|
+
if (beat.isBeat) {
|
|
197
|
+
beatFrameIndices.push(i);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if ((i + 1) % PREPROCESS_YIELD_EVERY === 0) {
|
|
201
|
+
await new Promise<void>(resolve => setImmediate(resolve));
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return { spectrums, beatFrameIndices };
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
async function prepareBackgroundForRender(params: {
|
|
209
|
+
useVideoBackground: boolean;
|
|
210
|
+
backgroundVideoPath: string | undefined;
|
|
211
|
+
backgroundImagePath: string | undefined;
|
|
212
|
+
beatFrameIndices: number[];
|
|
213
|
+
framesCount: number;
|
|
214
|
+
outputResolution: ReturnType<typeof getOutputResolution>;
|
|
215
|
+
fps: number;
|
|
216
|
+
autoEditVideo: boolean;
|
|
217
|
+
}): Promise<{
|
|
218
|
+
backgroundWidth: number;
|
|
219
|
+
backgroundHeight: number;
|
|
220
|
+
defaultColor: Color;
|
|
221
|
+
staticBackgroundBuffer: EncodedBmp;
|
|
222
|
+
videoFrameReader?: ReturnType<typeof spawnVideoFrameReader>;
|
|
223
|
+
videoFrameSize: number;
|
|
224
|
+
encodeVideoFrame?: (bgrBuffer: Buffer) => EncodedBmp;
|
|
225
|
+
concatFilePath?: string;
|
|
226
|
+
}> {
|
|
227
|
+
const {
|
|
228
|
+
useVideoBackground,
|
|
229
|
+
backgroundVideoPath,
|
|
230
|
+
backgroundImagePath,
|
|
231
|
+
beatFrameIndices,
|
|
232
|
+
framesCount,
|
|
233
|
+
outputResolution,
|
|
234
|
+
fps,
|
|
235
|
+
autoEditVideo,
|
|
236
|
+
} = params;
|
|
237
|
+
|
|
238
|
+
if (useVideoBackground && backgroundVideoPath) {
|
|
239
|
+
const videoInfo = await getVideoInfo(backgroundVideoPath);
|
|
240
|
+
const sceneChanges = autoEditVideo
|
|
241
|
+
? await detectSceneChanges(backgroundVideoPath)
|
|
242
|
+
: [];
|
|
243
|
+
const backgroundWidth = outputResolution?.width ?? videoInfo.width;
|
|
244
|
+
const backgroundHeight = outputResolution?.height ?? videoInfo.height;
|
|
245
|
+
|
|
246
|
+
const videoFrameSize = backgroundWidth * backgroundHeight * 3;
|
|
247
|
+
const encodeVideoFrame = createBgrFrameEncoder({ width: backgroundWidth, height: backgroundHeight });
|
|
248
|
+
|
|
249
|
+
const segments = buildBeatSyncedSegments(
|
|
250
|
+
beatFrameIndices,
|
|
251
|
+
framesCount,
|
|
252
|
+
sceneChanges,
|
|
253
|
+
videoInfo.duration,
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
const concatFilePath = writeConcatFile(segments, backgroundVideoPath, videoInfo.duration, fps);
|
|
257
|
+
|
|
258
|
+
const videoFrameReader = spawnConcatVideoFrameReader({
|
|
259
|
+
concatFilePath,
|
|
260
|
+
fps,
|
|
261
|
+
totalFrames: framesCount,
|
|
262
|
+
...(outputResolution && {
|
|
263
|
+
width: backgroundWidth,
|
|
264
|
+
height: backgroundHeight,
|
|
265
|
+
sourceWidth: videoInfo.width,
|
|
266
|
+
sourceHeight: videoInfo.height,
|
|
267
|
+
}),
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
const firstFrame = await readVideoFrame(videoFrameReader.stdout, videoFrameSize);
|
|
271
|
+
if (!firstFrame) {
|
|
272
|
+
throw new Error(`Could not read frames from video: ${backgroundVideoPath}`);
|
|
273
|
+
}
|
|
274
|
+
const defaultColor = getVideoFrameColor(firstFrame, backgroundWidth, backgroundHeight);
|
|
275
|
+
const staticBackgroundBuffer = encodeVideoFrame(firstFrame);
|
|
276
|
+
return {
|
|
277
|
+
backgroundWidth,
|
|
278
|
+
backgroundHeight,
|
|
279
|
+
defaultColor,
|
|
280
|
+
staticBackgroundBuffer,
|
|
281
|
+
videoFrameReader,
|
|
282
|
+
videoFrameSize,
|
|
283
|
+
encodeVideoFrame,
|
|
284
|
+
concatFilePath,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (!backgroundImagePath) {
|
|
289
|
+
throw new Error('Background image path is required when not using video background.');
|
|
290
|
+
}
|
|
291
|
+
const backgroundImageBmpBuffer = await convertToBmp(
|
|
292
|
+
backgroundImagePath,
|
|
293
|
+
outputResolution?.width,
|
|
294
|
+
outputResolution?.height,
|
|
295
|
+
);
|
|
296
|
+
const backgroundImage = parseImage(backgroundImageBmpBuffer);
|
|
297
|
+
const backgroundWidth = backgroundImage.width;
|
|
298
|
+
const backgroundHeight = backgroundImage.height;
|
|
299
|
+
const defaultColor = getImageColor(backgroundImage);
|
|
300
|
+
|
|
301
|
+
const bpmEncoder = createBpmEncoder({ width: backgroundWidth, height: backgroundHeight });
|
|
302
|
+
const staticBackgroundBuffer = bpmEncoder(backgroundImage.data);
|
|
303
|
+
return {
|
|
304
|
+
backgroundWidth,
|
|
305
|
+
backgroundHeight,
|
|
306
|
+
defaultColor,
|
|
307
|
+
staticBackgroundBuffer,
|
|
308
|
+
videoFrameSize: 0,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async function resolveBackgroundFrameBuffer(params: {
|
|
313
|
+
frameIndex: number;
|
|
314
|
+
useVideoBackground: boolean;
|
|
315
|
+
videoFrameReader?: ReturnType<typeof spawnVideoFrameReader>;
|
|
316
|
+
videoFrameSize: number;
|
|
317
|
+
encodeVideoFrame?: (bgrBuffer: Buffer) => EncodedBmp;
|
|
318
|
+
staticBackgroundBuffer: EncodedBmp;
|
|
319
|
+
}): Promise<EncodedBmp> {
|
|
320
|
+
const {
|
|
321
|
+
frameIndex,
|
|
322
|
+
useVideoBackground,
|
|
323
|
+
videoFrameReader,
|
|
324
|
+
videoFrameSize,
|
|
325
|
+
encodeVideoFrame,
|
|
326
|
+
staticBackgroundBuffer,
|
|
327
|
+
} = params;
|
|
328
|
+
|
|
329
|
+
if (useVideoBackground && videoFrameReader && encodeVideoFrame) {
|
|
330
|
+
if (frameIndex === 0) {
|
|
331
|
+
return staticBackgroundBuffer;
|
|
332
|
+
}
|
|
333
|
+
const videoFrame = await readVideoFrame(videoFrameReader.stdout, videoFrameSize);
|
|
334
|
+
return videoFrame
|
|
335
|
+
? encodeVideoFrame(videoFrame)
|
|
336
|
+
: staticBackgroundBuffer;
|
|
337
|
+
}
|
|
338
|
+
return staticBackgroundBuffer;
|
|
339
|
+
}
|
|
340
|
+
|
|
150
341
|
export const renderAudioVisualizer = (config: Config, onProgress?: (progress: number) => any, shouldStop?: () => boolean) =>
|
|
151
342
|
new Promise<number>(async (resolve) => {
|
|
152
|
-
// Validate that only one visualizer type is specified
|
|
153
343
|
if (config.outVideo.spectrum && config.outVideo.polar) {
|
|
154
344
|
throw new Error('Cannot use both "spectrum" and "polar" options. Please specify only one visualizer type.');
|
|
155
345
|
}
|
|
346
|
+
if (!config.image && !config.video) {
|
|
347
|
+
throw new Error('Either "image" or "video" must be specified as the background source.');
|
|
348
|
+
}
|
|
349
|
+
if (config.image && config.video) {
|
|
350
|
+
throw new Error('Cannot use both "image" and "video" options. Please specify only one background source.');
|
|
351
|
+
}
|
|
156
352
|
|
|
157
353
|
const audioFilePath = getAudioFilePath(config);
|
|
158
|
-
const backgroundImagePath = getBackgroundImagePath(config);
|
|
159
354
|
const outVideoPath = getOutVideoPath(config);
|
|
355
|
+
const backgroundVideoPath = getBackgroundVideoPath(config);
|
|
356
|
+
const backgroundImagePath = getBackgroundImagePath(config);
|
|
357
|
+
const useVideoBackground = !!backgroundVideoPath;
|
|
160
358
|
|
|
161
|
-
const backgroundImageBmpBuffer = await convertToBmp(backgroundImagePath);
|
|
162
|
-
const backgroundImage = parseImage(backgroundImageBmpBuffer);
|
|
163
359
|
const audioReader = await createAudioBuffer(audioFilePath, FFMPEG_FORMAT);
|
|
164
360
|
const audioBuffer = audioReader.audioBuffer;
|
|
165
361
|
const sampleRate = audioReader.sampleRate;
|
|
@@ -173,12 +369,38 @@ export const renderAudioVisualizer = (config: Config, onProgress?: (progress: nu
|
|
|
173
369
|
const ffmpeg_preset = getFfmpeg_preset(config);
|
|
174
370
|
const frame_processing_delay = getFrame_processing_delay(config);
|
|
175
371
|
|
|
176
|
-
const createVisualizerFrame = createVisualizerFrameGenerator(config, backgroundImage, spectrumBusMargin);
|
|
177
|
-
|
|
178
372
|
const audioDuration = audioBuffer.length / sampleRate;
|
|
179
373
|
const framesCount = Math.trunc(audioDuration * FPS);
|
|
180
|
-
const
|
|
181
|
-
|
|
374
|
+
const outputResolution = getOutputResolution(config);
|
|
375
|
+
|
|
376
|
+
if (onProgress) {
|
|
377
|
+
onProgress(0);
|
|
378
|
+
}
|
|
379
|
+
const preprocessed = await preProcessAudio(audioBuffer, sampleRate, FPS, framesCount);
|
|
380
|
+
|
|
381
|
+
const {
|
|
382
|
+
backgroundWidth,
|
|
383
|
+
backgroundHeight,
|
|
384
|
+
defaultColor,
|
|
385
|
+
staticBackgroundBuffer,
|
|
386
|
+
videoFrameReader,
|
|
387
|
+
videoFrameSize,
|
|
388
|
+
encodeVideoFrame,
|
|
389
|
+
concatFilePath,
|
|
390
|
+
} = await prepareBackgroundForRender({
|
|
391
|
+
useVideoBackground,
|
|
392
|
+
backgroundVideoPath,
|
|
393
|
+
backgroundImagePath,
|
|
394
|
+
beatFrameIndices: preprocessed.beatFrameIndices,
|
|
395
|
+
framesCount,
|
|
396
|
+
outputResolution,
|
|
397
|
+
fps: FPS,
|
|
398
|
+
autoEditVideo: getAutoEditVideo(config),
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
const createVisualizerFrame = createVisualizerFrameGenerator(
|
|
402
|
+
config, backgroundWidth, backgroundHeight, defaultColor, spectrumBusMargin
|
|
403
|
+
);
|
|
182
404
|
|
|
183
405
|
const ffmpegVideoWriter = spawnFfmpegVideoWriter({
|
|
184
406
|
audioFilename: audioFilePath,
|
|
@@ -190,18 +412,18 @@ export const renderAudioVisualizer = (config: Config, onProgress?: (progress: nu
|
|
|
190
412
|
});
|
|
191
413
|
ffmpegVideoWriter.on('exit', (code: number) => resolve(code));
|
|
192
414
|
|
|
193
|
-
const bpmEncoder = createBpmEncoder({ width: backgroundImage.width, height: backgroundImage.height });
|
|
194
|
-
const backgroundImageBuffer = bpmEncoder(backgroundImage.data);
|
|
195
|
-
const skipFramesCount = FPS < 45 ? 1 : 2;
|
|
196
|
-
const processSpectrum = createSpectrumsProcessor(sampleRate, skipFramesCount);
|
|
197
|
-
|
|
198
415
|
for (let i = 0; i < framesCount; i++) {
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
416
|
+
const spectrum = preprocessed.spectrums[i];
|
|
417
|
+
|
|
418
|
+
const backgroundImageBuffer = await resolveBackgroundFrameBuffer({
|
|
419
|
+
frameIndex: i,
|
|
420
|
+
useVideoBackground,
|
|
421
|
+
videoFrameReader,
|
|
422
|
+
videoFrameSize,
|
|
423
|
+
encodeVideoFrame,
|
|
424
|
+
staticBackgroundBuffer,
|
|
425
|
+
});
|
|
202
426
|
|
|
203
|
-
const audioDataParser = () => Array.from(processingBuffer);
|
|
204
|
-
const spectrum = processSpectrum(audioDataParser);
|
|
205
427
|
const commonVisualizerFrameProps: CommonVisualizerFrameProps = {
|
|
206
428
|
backgroundImageBuffer,
|
|
207
429
|
spectrum,
|
|
@@ -219,5 +441,11 @@ export const renderAudioVisualizer = (config: Config, onProgress?: (progress: nu
|
|
|
219
441
|
}
|
|
220
442
|
}
|
|
221
443
|
|
|
444
|
+
if (videoFrameReader) {
|
|
445
|
+
videoFrameReader.kill();
|
|
446
|
+
}
|
|
447
|
+
if (concatFilePath) {
|
|
448
|
+
cleanupConcatFile(concatFilePath);
|
|
449
|
+
}
|
|
222
450
|
ffmpegVideoWriter.stdin.end();
|
|
223
451
|
});
|