nodejs-audio-visualizer 4.2.4 → 4.2.6

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/dist/index.js CHANGED
@@ -1,467 +1,498 @@
1
- "use strict";
2
- var __assign = (this && this.__assign) || function () {
3
- __assign = Object.assign || function(t) {
4
- for (var s, i = 1, n = arguments.length; i < n; i++) {
5
- s = arguments[i];
6
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
- t[p] = s[p];
8
- }
9
- return t;
10
- };
11
- return __assign.apply(this, arguments);
12
- };
13
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
15
- return new (P || (P = Promise))(function (resolve, reject) {
16
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
17
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
18
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
19
- step((generator = generator.apply(thisArg, _arguments || [])).next());
20
- });
21
- };
22
- var __generator = (this && this.__generator) || function (thisArg, body) {
23
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
24
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
25
- function verb(n) { return function (v) { return step([n, v]); }; }
26
- function step(op) {
27
- if (f) throw new TypeError("Generator is already executing.");
28
- while (_) try {
29
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
30
- if (y = 0, t) op = [op[0] & 2, t.value];
31
- switch (op[0]) {
32
- case 0: case 1: t = op; break;
33
- case 4: _.label++; return { value: op[1], done: false };
34
- case 5: _.label++; y = op[1]; op = [0]; continue;
35
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
36
- default:
37
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
38
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
39
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
40
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
41
- if (t[2]) _.ops.pop();
42
- _.trys.pop(); continue;
43
- }
44
- op = body.call(thisArg, _);
45
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
46
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
47
- }
48
- };
49
- var __importDefault = (this && this.__importDefault) || function (mod) {
50
- return (mod && mod.__esModule) ? mod : { "default": mod };
51
- };
52
- Object.defineProperty(exports, "__esModule", { value: true });
53
- var path_1 = __importDefault(require("path"));
54
- var config_1 = require("./config");
55
- var audio_1 = require("./audio");
56
- var image_1 = require("./image");
57
- var video_1 = require("./video");
58
- var bpmEncoder_1 = require("./bpmEncoder");
59
- var beats_1 = require("./beats");
60
- var highlight_1 = require("./highlight");
61
- var waitForEventLoop_1 = require("./waitForEventLoop");
62
- var highlight_2 = require("./highlight");
63
- exports.computeHighlightSlice = highlight_2.computeHighlightSlice;
64
- exports.HIGHLIGHT_DURATION_SEC = highlight_2.HIGHLIGHT_DURATION_SEC;
65
- exports.PCM_FORMAT = {
66
- bit: 8,
67
- sign: 'u',
68
- parseFunction: audio_1.bufferToUInt8
69
- };
70
- var FFMPEG_FORMAT = "" + exports.PCM_FORMAT.sign + exports.PCM_FORMAT.bit;
71
- var PROCESSING_BUFFER_SIZE = Math.pow(2, 12);
72
- var sleep = function (timeout) {
73
- return new Promise(function (resolve) { return setTimeout(resolve, timeout); });
74
- };
75
- var createVisualizerFrameGenerator = function (config, backgroundWidth, backgroundHeight, defaultColor, spectrumBusMargin) {
76
- if (!config.outVideo.spectrum) {
77
- var createPolarVisualizerFrame_1 = image_1.createPolarVisualizerFrameGenerator();
78
- var polarX_1 = config_1.getPolarXAbsolute(config, backgroundWidth);
79
- var polarY_1 = config_1.getPolarYAbsolute(config, backgroundHeight);
80
- var polarInnerRadius_1 = config_1.getPolarInnerRadius(config);
81
- var polarMaxBarLength_1 = config_1.getPolarMaxBarLength(config);
82
- var polarBarWidth_1 = config_1.getPolarBarWidth(config);
83
- var polarColor_1 = config_1.getPolarColor(config) || image_1.invertColor(defaultColor);
84
- var polarEffect_1 = config_1.getPolarEffect(config);
85
- var polarOpacity_1 = config_1.getPolarOpacityParsed(config);
86
- return function (params) {
87
- return createPolarVisualizerFrame_1(__assign(__assign({}, params), { centerX: polarX_1, centerY: polarY_1, innerRadius: polarInnerRadius_1, maxBarLength: polarMaxBarLength_1, barWidth: polarBarWidth_1, color: polarColor_1, opacity: polarOpacity_1, spectrumEffect: polarEffect_1 }));
88
- };
89
- }
90
- var createSpectrumVisualizerFrame = image_1.createSpectrumVisualizerFrameGenerator();
91
- var spectrumWidth = config_1.getSpectrumWidthAbsolute(config, backgroundWidth);
92
- var spectrumHeight = config_1.getSpectrumHeightAbsolute(config, backgroundHeight);
93
- var spectrumX = config_1.getSpectrumXAbsolute(config, spectrumWidth, backgroundWidth);
94
- var spectrumY = config_1.getSpectrumYAbsolute(config, spectrumHeight, backgroundHeight);
95
- var spectrumRotation = config_1.getSpectrumRotation(config);
96
- var spectrumColor = config_1.getSpectrumColor(config) || image_1.invertColor(defaultColor);
97
- var spectrumEffect = config_1.getSpectrumEffect(config);
98
- var spectrumOpacity = config_1.getSpectrumOpacityParsed(config);
99
- return function (params) {
100
- return createSpectrumVisualizerFrame(__assign(__assign({}, params), { size: { width: spectrumWidth, height: spectrumHeight }, position: { x: spectrumX, y: spectrumY }, rotation: spectrumRotation, margin: spectrumBusMargin, color: spectrumColor, opacity: spectrumOpacity, spectrumEffect: spectrumEffect }));
101
- };
102
- };
103
- var PRE_PROCESS_PROGRESS_SHARE = 60;
104
- var preProcessAudio = function (audioBuffer, sampleRate, fps, framesCount, onPreProcessProgress) { return __awaiter(void 0, void 0, void 0, function () {
105
- var t, audioDataStep, processingBuffer, skipFramesCount, processSpectrum, detectBeat, spectrums, beatFrameIndices, beatEvents, nextPreProcessMilestone, i, currentFrameData, audioDataParser, spectrum, beat, p;
106
- var _a, _b;
107
- return __generator(this, function (_c) {
108
- switch (_c.label) {
109
- case 0:
110
- if (framesCount === 0) {
111
- for (t = 1; t <= PRE_PROCESS_PROGRESS_SHARE; t += 1) {
112
- (_a = onPreProcessProgress) === null || _a === void 0 ? void 0 : _a(t);
113
- }
114
- return [2 /*return*/, { spectrums: [], beatFrameIndices: [], beatEvents: [] }];
115
- }
116
- audioDataStep = Math.trunc(audioBuffer.length / framesCount);
117
- processingBuffer = new Float32Array(PROCESSING_BUFFER_SIZE).fill(0);
118
- skipFramesCount = fps < 45 ? 1 : 2;
119
- processSpectrum = audio_1.createSpectrumsProcessor(sampleRate, skipFramesCount);
120
- detectBeat = beats_1.createBeatDetector(fps);
121
- spectrums = [];
122
- beatFrameIndices = [];
123
- beatEvents = [];
124
- nextPreProcessMilestone = 1;
125
- i = 0;
126
- _c.label = 1;
127
- case 1:
128
- if (!(i < framesCount)) return [3 /*break*/, 4];
129
- currentFrameData = exports.PCM_FORMAT.parseFunction(audioBuffer, i * audioDataStep, i * audioDataStep + audioDataStep);
130
- processingBuffer.copyWithin(0, currentFrameData.length);
131
- processingBuffer.set(currentFrameData, PROCESSING_BUFFER_SIZE - currentFrameData.length);
132
- audioDataParser = function () { return Array.from(processingBuffer); };
133
- spectrum = processSpectrum(audioDataParser);
134
- beat = detectBeat(spectrum);
135
- spectrums.push(spectrum);
136
- if (beat.isBeat) {
137
- beatFrameIndices.push(i);
138
- beatEvents.push({ frameIndex: i, intensity: beat.intensity });
139
- }
140
- p = PRE_PROCESS_PROGRESS_SHARE * (i + 1) / framesCount;
141
- while (nextPreProcessMilestone <= PRE_PROCESS_PROGRESS_SHARE
142
- && p >= nextPreProcessMilestone) {
143
- (_b = onPreProcessProgress) === null || _b === void 0 ? void 0 : _b(nextPreProcessMilestone);
144
- nextPreProcessMilestone += 1;
145
- }
146
- return [4 /*yield*/, waitForEventLoop_1.waitForEventLoop()];
147
- case 2:
148
- _c.sent();
149
- _c.label = 3;
150
- case 3:
151
- i++;
152
- return [3 /*break*/, 1];
153
- case 4: return [2 /*return*/, { spectrums: spectrums, beatFrameIndices: beatFrameIndices, beatEvents: beatEvents }];
154
- }
155
- });
156
- }); };
157
- function prepareBackgroundForRender(params) {
158
- var _a, _b, _c, _d, _e, _f;
159
- return __awaiter(this, void 0, void 0, function () {
160
- var useVideoBackground, backgroundVideoPath, backgroundImagePath, beatFrameIndices, framesCount, outputResolution, fps, autoEditVideo, videoInfo, sceneChanges, _g, backgroundWidth_1, backgroundHeight_1, videoFrameSize, encodeVideoFrame, segments, concatFilePath, videoFrameReader, firstFrame, defaultColor_1, staticBackgroundBuffer_1, backgroundImageBmpBuffer, backgroundImage, backgroundWidth, backgroundHeight, defaultColor, bpmEncoder, staticBackgroundBuffer;
161
- return __generator(this, function (_h) {
162
- switch (_h.label) {
163
- case 0:
164
- useVideoBackground = params.useVideoBackground, backgroundVideoPath = params.backgroundVideoPath, backgroundImagePath = params.backgroundImagePath, beatFrameIndices = params.beatFrameIndices, framesCount = params.framesCount, outputResolution = params.outputResolution, fps = params.fps, autoEditVideo = params.autoEditVideo;
165
- if (!(useVideoBackground && backgroundVideoPath)) return [3 /*break*/, 6];
166
- return [4 /*yield*/, video_1.getVideoInfo(backgroundVideoPath)];
167
- case 1:
168
- videoInfo = _h.sent();
169
- if (!autoEditVideo) return [3 /*break*/, 3];
170
- return [4 /*yield*/, video_1.detectSceneChanges(backgroundVideoPath)];
171
- case 2:
172
- _g = _h.sent();
173
- return [3 /*break*/, 4];
174
- case 3:
175
- _g = [];
176
- _h.label = 4;
177
- case 4:
178
- sceneChanges = _g;
179
- backgroundWidth_1 = (_b = (_a = outputResolution) === null || _a === void 0 ? void 0 : _a.width, (_b !== null && _b !== void 0 ? _b : videoInfo.width));
180
- backgroundHeight_1 = (_d = (_c = outputResolution) === null || _c === void 0 ? void 0 : _c.height, (_d !== null && _d !== void 0 ? _d : videoInfo.height));
181
- videoFrameSize = backgroundWidth_1 * backgroundHeight_1 * 3;
182
- encodeVideoFrame = bpmEncoder_1.createBgrFrameEncoder({ width: backgroundWidth_1, height: backgroundHeight_1 });
183
- segments = video_1.buildBeatSyncedSegments(beatFrameIndices, framesCount, sceneChanges, videoInfo.duration);
184
- concatFilePath = video_1.writeConcatFile(segments, backgroundVideoPath, videoInfo.duration, fps);
185
- videoFrameReader = video_1.spawnConcatVideoFrameReader(__assign({ concatFilePath: concatFilePath,
186
- fps: fps, totalFrames: framesCount }, (outputResolution && {
187
- width: backgroundWidth_1,
188
- height: backgroundHeight_1,
189
- sourceWidth: videoInfo.width,
190
- sourceHeight: videoInfo.height,
191
- })));
192
- return [4 /*yield*/, video_1.readVideoFrame(videoFrameReader.stdout, videoFrameSize)];
193
- case 5:
194
- firstFrame = _h.sent();
195
- if (!firstFrame) {
196
- throw new Error("Could not read frames from video: " + backgroundVideoPath);
197
- }
198
- defaultColor_1 = image_1.getVideoFrameColor(firstFrame, backgroundWidth_1, backgroundHeight_1);
199
- staticBackgroundBuffer_1 = encodeVideoFrame(firstFrame);
200
- return [2 /*return*/, {
201
- backgroundWidth: backgroundWidth_1,
202
- backgroundHeight: backgroundHeight_1,
203
- defaultColor: defaultColor_1,
204
- staticBackgroundBuffer: staticBackgroundBuffer_1,
205
- videoFrameReader: videoFrameReader,
206
- videoFrameSize: videoFrameSize,
207
- encodeVideoFrame: encodeVideoFrame,
208
- concatFilePath: concatFilePath,
209
- }];
210
- case 6:
211
- if (!backgroundImagePath) {
212
- throw new Error('Background image path is required when not using video background.');
213
- }
214
- return [4 /*yield*/, image_1.convertToBmp(backgroundImagePath, (_e = outputResolution) === null || _e === void 0 ? void 0 : _e.width, (_f = outputResolution) === null || _f === void 0 ? void 0 : _f.height)];
215
- case 7:
216
- backgroundImageBmpBuffer = _h.sent();
217
- backgroundImage = image_1.parseImage(backgroundImageBmpBuffer);
218
- backgroundWidth = backgroundImage.width;
219
- backgroundHeight = backgroundImage.height;
220
- defaultColor = image_1.getImageColor(backgroundImage);
221
- bpmEncoder = bpmEncoder_1.createBpmEncoder({ width: backgroundWidth, height: backgroundHeight });
222
- staticBackgroundBuffer = bpmEncoder(backgroundImage.data);
223
- return [2 /*return*/, {
224
- backgroundWidth: backgroundWidth,
225
- backgroundHeight: backgroundHeight,
226
- defaultColor: defaultColor,
227
- staticBackgroundBuffer: staticBackgroundBuffer,
228
- videoFrameSize: 0,
229
- }];
230
- }
231
- });
232
- });
233
- }
234
- function resolveBackgroundFrameBuffer(params) {
235
- return __awaiter(this, void 0, void 0, function () {
236
- var frameIndex, useVideoBackground, videoFrameReader, videoFrameSize, encodeVideoFrame, staticBackgroundBuffer, videoFrame;
237
- return __generator(this, function (_a) {
238
- switch (_a.label) {
239
- case 0:
240
- frameIndex = params.frameIndex, useVideoBackground = params.useVideoBackground, videoFrameReader = params.videoFrameReader, videoFrameSize = params.videoFrameSize, encodeVideoFrame = params.encodeVideoFrame, staticBackgroundBuffer = params.staticBackgroundBuffer;
241
- if (!(useVideoBackground && videoFrameReader && encodeVideoFrame)) return [3 /*break*/, 2];
242
- if (frameIndex === 0) {
243
- return [2 /*return*/, staticBackgroundBuffer];
244
- }
245
- return [4 /*yield*/, video_1.readVideoFrame(videoFrameReader.stdout, videoFrameSize)];
246
- case 1:
247
- videoFrame = _a.sent();
248
- return [2 /*return*/, videoFrame
249
- ? encodeVideoFrame(videoFrame)
250
- : staticBackgroundBuffer];
251
- case 2: return [2 /*return*/, staticBackgroundBuffer];
252
- }
253
- });
254
- });
255
- }
256
- exports.renderAudioVisualizer = function (config, onProgress, shouldStop) {
257
- return new Promise(function (resolve) { return __awaiter(void 0, void 0, void 0, function () {
258
- var audioFilePath, outVideoPath, backgroundVideoPath, backgroundImagePath, useVideoBackground, audioReader, audioBuffer, sampleRate, spectrumBusMargin, FPS, ffmpeg_cfr, ffmpeg_preset, frame_processing_delay, audioDuration, framesCount, outputResolution, preprocessed, autoHighlight, spectrumsForRender, beatIndicesForRender, framesCountForRender, highlightSlice, separateHighlightFiles, passes, progressDenominator, progressFrameBase, lastExitCode, outputVideoFiles, _loop_1, _i, passes_1, pass, state_1;
259
- return __generator(this, function (_a) {
260
- switch (_a.label) {
261
- case 0:
262
- if (config.outVideo.spectrum && config.outVideo.polar) {
263
- throw new Error('Cannot use both "spectrum" and "polar" options. Please specify only one visualizer type.');
264
- }
265
- if (!config.image && !config.video) {
266
- throw new Error('Either "image" or "video" must be specified as the background source.');
267
- }
268
- if (config.image && config.video) {
269
- throw new Error('Cannot use both "image" and "video" options. Please specify only one background source.');
270
- }
271
- audioFilePath = config_1.getAudioFilePath(config);
272
- outVideoPath = config_1.getOutVideoPath(config);
273
- backgroundVideoPath = config_1.getBackgroundVideoPath(config);
274
- backgroundImagePath = config_1.getBackgroundImagePath(config);
275
- useVideoBackground = !!backgroundVideoPath;
276
- return [4 /*yield*/, audio_1.createAudioBuffer(audioFilePath, FFMPEG_FORMAT)];
277
- case 1:
278
- audioReader = _a.sent();
279
- audioBuffer = audioReader.audioBuffer;
280
- sampleRate = audioReader.sampleRate;
281
- if (!sampleRate) {
282
- throw new Error('ffmpeg didn\'t show audio sample rate');
283
- }
284
- spectrumBusMargin = config_1.getSpectrumBusMargin();
285
- FPS = config_1.getFPS(config);
286
- ffmpeg_cfr = config_1.getFfmpeg_cfr(config);
287
- ffmpeg_preset = config_1.getFfmpeg_preset(config);
288
- frame_processing_delay = config_1.getFrame_processing_delay(config);
289
- audioDuration = audioBuffer.length / sampleRate;
290
- framesCount = Math.trunc(audioDuration * FPS);
291
- outputResolution = config_1.getOutputResolution(config);
292
- if (onProgress) {
293
- onProgress(0);
294
- }
295
- return [4 /*yield*/, preProcessAudio(audioBuffer, sampleRate, FPS, framesCount, onProgress)];
296
- case 2:
297
- preprocessed = _a.sent();
298
- autoHighlight = config_1.getAudioAutoHighlight(config);
299
- spectrumsForRender = preprocessed.spectrums;
300
- beatIndicesForRender = preprocessed.beatFrameIndices;
301
- framesCountForRender = framesCount;
302
- if (!autoHighlight) return [3 /*break*/, 4];
303
- return [4 /*yield*/, highlight_1.computeHighlightSlice(FPS, framesCount, preprocessed.spectrums, preprocessed.beatEvents, config_1.getAudioAutoHighlightCount(config))];
304
- case 3:
305
- highlightSlice = _a.sent();
306
- spectrumsForRender = highlightSlice.spectrums;
307
- beatIndicesForRender = highlightSlice.beatFrameIndices;
308
- framesCountForRender = highlightSlice.highlightFrames;
309
- _a.label = 4;
310
- case 4:
311
- separateHighlightFiles = autoHighlight &&
312
- highlightSlice &&
313
- highlightSlice.runs.length > 1;
314
- passes = separateHighlightFiles && highlightSlice
315
- ? highlightSlice.runs.map(function (run, i) {
316
- var dir = path_1.default.dirname(outVideoPath);
317
- var ext = path_1.default.extname(outVideoPath);
318
- var base = path_1.default.basename(outVideoPath, ext);
319
- var numberedPath = path_1.default.resolve(dir, base + "-" + (i + 1) + ext);
320
- return {
321
- outPath: numberedPath,
322
- spectrums: run.spectrums,
323
- beatIndices: run.beatFrameIndices,
324
- frameCount: run.highlightFrames,
325
- audioSegment: run.audioSegment,
326
- };
327
- })
328
- : [
329
- {
330
- outPath: outVideoPath,
331
- spectrums: spectrumsForRender,
332
- beatIndices: beatIndicesForRender,
333
- frameCount: framesCountForRender,
334
- audioSegment: autoHighlight &&
335
- highlightSlice &&
336
- highlightSlice.audioSegments.length > 0
337
- ? highlightSlice.audioSegments[0]
338
- : undefined,
339
- },
340
- ];
341
- progressDenominator = passes.reduce(function (s, p) { return s + p.frameCount; }, 0) + passes.length;
342
- progressFrameBase = 0;
343
- lastExitCode = 0;
344
- outputVideoFiles = [];
345
- _loop_1 = function (pass) {
346
- var _a, backgroundWidth, backgroundHeight, defaultColor, staticBackgroundBuffer, videoFrameReader, videoFrameSize, encodeVideoFrame, concatFilePath, createVisualizerFrame, ffmpegVideoWriter, exitPromise, stoppedEarly, i, spectrum, backgroundImageBuffer, commonVisualizerFrameProps, frameImage, isFrameProcessed;
347
- return __generator(this, function (_b) {
348
- switch (_b.label) {
349
- case 0: return [4 /*yield*/, prepareBackgroundForRender({
350
- useVideoBackground: useVideoBackground,
351
- backgroundVideoPath: backgroundVideoPath,
352
- backgroundImagePath: backgroundImagePath,
353
- beatFrameIndices: pass.beatIndices,
354
- framesCount: pass.frameCount,
355
- outputResolution: outputResolution,
356
- fps: FPS,
357
- autoEditVideo: config_1.getAutoEditVideo(config),
358
- })];
359
- case 1:
360
- _a = _b.sent(), backgroundWidth = _a.backgroundWidth, backgroundHeight = _a.backgroundHeight, defaultColor = _a.defaultColor, staticBackgroundBuffer = _a.staticBackgroundBuffer, videoFrameReader = _a.videoFrameReader, videoFrameSize = _a.videoFrameSize, encodeVideoFrame = _a.encodeVideoFrame, concatFilePath = _a.concatFilePath;
361
- createVisualizerFrame = createVisualizerFrameGenerator(config, backgroundWidth, backgroundHeight, defaultColor, spectrumBusMargin);
362
- ffmpegVideoWriter = video_1.spawnFfmpegVideoWriter(__assign(__assign(__assign(__assign({ audioFilename: audioFilePath, videoFileName: pass.outPath, fps: FPS }, (pass.audioSegment && { audioSegment: pass.audioSegment })), (!!onProgress && {
363
- onStderr: video_1.getProgress(function (currentFrame) {
364
- var g = progressFrameBase + currentFrame;
365
- onProgress(+(Math.min(100, PRE_PROCESS_PROGRESS_SHARE
366
- + (90 * g) / progressDenominator)).toFixed(2));
367
- }),
368
- })), (ffmpeg_cfr && { crf: ffmpeg_cfr })), (ffmpeg_preset && { preset: ffmpeg_preset })));
369
- exitPromise = new Promise(function (res) {
370
- var settled = false;
371
- ffmpegVideoWriter.on('exit', function (code) {
372
- if (settled) {
373
- return;
374
- }
375
- settled = true;
376
- res((code !== null && code !== void 0 ? code : 0));
377
- });
378
- });
379
- stoppedEarly = false;
380
- i = 0;
381
- _b.label = 2;
382
- case 2:
383
- if (!(i < pass.frameCount)) return [3 /*break*/, 10];
384
- spectrum = pass.spectrums[i];
385
- return [4 /*yield*/, resolveBackgroundFrameBuffer({
386
- frameIndex: i,
387
- useVideoBackground: useVideoBackground,
388
- videoFrameReader: videoFrameReader,
389
- videoFrameSize: videoFrameSize,
390
- encodeVideoFrame: encodeVideoFrame,
391
- staticBackgroundBuffer: staticBackgroundBuffer,
392
- })];
393
- case 3:
394
- backgroundImageBuffer = _b.sent();
395
- commonVisualizerFrameProps = {
396
- backgroundImageBuffer: backgroundImageBuffer,
397
- spectrum: spectrum,
398
- };
399
- frameImage = createVisualizerFrame(commonVisualizerFrameProps);
400
- isFrameProcessed = ffmpegVideoWriter.stdin.write(frameImage.data);
401
- if (!!isFrameProcessed) return [3 /*break*/, 5];
402
- return [4 /*yield*/, video_1.waitDrain(ffmpegVideoWriter.stdin)];
403
- case 4:
404
- _b.sent();
405
- _b.label = 5;
406
- case 5:
407
- if (shouldStop && shouldStop()) {
408
- stoppedEarly = true;
409
- return [3 /*break*/, 10];
410
- }
411
- if (!frame_processing_delay) return [3 /*break*/, 7];
412
- return [4 /*yield*/, sleep(frame_processing_delay)];
413
- case 6:
414
- _b.sent();
415
- _b.label = 7;
416
- case 7: return [4 /*yield*/, waitForEventLoop_1.waitForEventLoop()];
417
- case 8:
418
- _b.sent();
419
- _b.label = 9;
420
- case 9:
421
- i++;
422
- return [3 /*break*/, 2];
423
- case 10:
424
- if (videoFrameReader) {
425
- videoFrameReader.kill();
426
- }
427
- if (concatFilePath) {
428
- video_1.cleanupConcatFile(concatFilePath);
429
- }
430
- ffmpegVideoWriter.stdin.end();
431
- return [4 /*yield*/, exitPromise];
432
- case 11:
433
- lastExitCode = _b.sent();
434
- progressFrameBase += pass.frameCount + 1;
435
- if (lastExitCode === 0 && !stoppedEarly) {
436
- outputVideoFiles.push(pass.outPath);
437
- }
438
- if (stoppedEarly || lastExitCode !== 0) {
439
- return [2 /*return*/, "break-passLoop"];
440
- }
441
- return [2 /*return*/];
442
- }
443
- });
444
- };
445
- _i = 0, passes_1 = passes;
446
- _a.label = 5;
447
- case 5:
448
- if (!(_i < passes_1.length)) return [3 /*break*/, 8];
449
- pass = passes_1[_i];
450
- return [5 /*yield**/, _loop_1(pass)];
451
- case 6:
452
- state_1 = _a.sent();
453
- switch (state_1) {
454
- case "break-passLoop": return [3 /*break*/, 8];
455
- }
456
- _a.label = 7;
457
- case 7:
458
- _i++;
459
- return [3 /*break*/, 5];
460
- case 8:
461
- resolve({ exitCode: lastExitCode, outputVideoFiles: outputVideoFiles });
462
- return [2 /*return*/];
463
- }
464
- });
465
- }); });
466
- };
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
15
+ return new (P || (P = Promise))(function (resolve, reject) {
16
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
17
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
18
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
19
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
20
+ });
21
+ };
22
+ var __generator = (this && this.__generator) || function (thisArg, body) {
23
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
24
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
25
+ function verb(n) { return function (v) { return step([n, v]); }; }
26
+ function step(op) {
27
+ if (f) throw new TypeError("Generator is already executing.");
28
+ while (_) try {
29
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
30
+ if (y = 0, t) op = [op[0] & 2, t.value];
31
+ switch (op[0]) {
32
+ case 0: case 1: t = op; break;
33
+ case 4: _.label++; return { value: op[1], done: false };
34
+ case 5: _.label++; y = op[1]; op = [0]; continue;
35
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
36
+ default:
37
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
38
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
39
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
40
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
41
+ if (t[2]) _.ops.pop();
42
+ _.trys.pop(); continue;
43
+ }
44
+ op = body.call(thisArg, _);
45
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
46
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
47
+ }
48
+ };
49
+ var __importDefault = (this && this.__importDefault) || function (mod) {
50
+ return (mod && mod.__esModule) ? mod : { "default": mod };
51
+ };
52
+ Object.defineProperty(exports, "__esModule", { value: true });
53
+ var path_1 = __importDefault(require("path"));
54
+ var config_1 = require("./config");
55
+ var audio_1 = require("./audio");
56
+ var image_1 = require("./image");
57
+ var video_1 = require("./video");
58
+ var bpmEncoder_1 = require("./bpmEncoder");
59
+ var beats_1 = require("./beats");
60
+ var highlight_1 = require("./highlight");
61
+ var waitForEventLoop_1 = require("./waitForEventLoop");
62
+ var highlight_2 = require("./highlight");
63
+ exports.computeHighlightSlice = highlight_2.computeHighlightSlice;
64
+ exports.HIGHLIGHT_DURATION_SEC = highlight_2.HIGHLIGHT_DURATION_SEC;
65
+ exports.PCM_FORMAT = {
66
+ bit: 8,
67
+ sign: 'u',
68
+ parseFunction: audio_1.bufferToUInt8
69
+ };
70
+ var FFMPEG_FORMAT = "" + exports.PCM_FORMAT.sign + exports.PCM_FORMAT.bit;
71
+ var PROCESSING_BUFFER_SIZE = Math.pow(2, 12);
72
+ var sleep = function (timeout) {
73
+ return new Promise(function (resolve) { return setTimeout(resolve, timeout); });
74
+ };
75
+ var createVisualizerFrameGenerator = function (config, backgroundWidth, backgroundHeight, defaultColor, spectrumBusMargin) {
76
+ if (!config.outVideo.spectrum) {
77
+ var createPolarVisualizerFrame_1 = image_1.createPolarVisualizerFrameGenerator();
78
+ var polarX_1 = config_1.getPolarXAbsolute(config, backgroundWidth);
79
+ var polarY_1 = config_1.getPolarYAbsolute(config, backgroundHeight);
80
+ var polarInnerRadius_1 = config_1.getPolarInnerRadius(config);
81
+ var polarMaxBarLength_1 = config_1.getPolarMaxBarLength(config);
82
+ var polarBarWidth_1 = config_1.getPolarBarWidth(config);
83
+ var polarColor_1 = config_1.getPolarColor(config) || image_1.invertColor(defaultColor);
84
+ var polarEffect_1 = config_1.getPolarEffect(config);
85
+ var polarOpacity_1 = config_1.getPolarOpacityParsed(config);
86
+ return function (params) {
87
+ return createPolarVisualizerFrame_1(__assign(__assign({}, params), { centerX: polarX_1, centerY: polarY_1, innerRadius: polarInnerRadius_1, maxBarLength: polarMaxBarLength_1, barWidth: polarBarWidth_1, color: polarColor_1, opacity: polarOpacity_1, spectrumEffect: polarEffect_1 }));
88
+ };
89
+ }
90
+ var createSpectrumVisualizerFrame = image_1.createSpectrumVisualizerFrameGenerator();
91
+ var spectrumWidth = config_1.getSpectrumWidthAbsolute(config, backgroundWidth);
92
+ var spectrumHeight = config_1.getSpectrumHeightAbsolute(config, backgroundHeight);
93
+ var spectrumX = config_1.getSpectrumXAbsolute(config, spectrumWidth, backgroundWidth);
94
+ var spectrumY = config_1.getSpectrumYAbsolute(config, spectrumHeight, backgroundHeight);
95
+ var spectrumRotation = config_1.getSpectrumRotation(config);
96
+ var spectrumColor = config_1.getSpectrumColor(config) || image_1.invertColor(defaultColor);
97
+ var spectrumEffect = config_1.getSpectrumEffect(config);
98
+ var spectrumOpacity = config_1.getSpectrumOpacityParsed(config);
99
+ return function (params) {
100
+ return createSpectrumVisualizerFrame(__assign(__assign({}, params), { size: { width: spectrumWidth, height: spectrumHeight }, position: { x: spectrumX, y: spectrumY }, rotation: spectrumRotation, margin: spectrumBusMargin, color: spectrumColor, opacity: spectrumOpacity, spectrumEffect: spectrumEffect }));
101
+ };
102
+ };
103
+ var PRE_PROCESS_PROGRESS_SHARE = 60;
104
+ var POST_AUDIO_PROGRESS_SHARE = 10;
105
+ var RENDER_PROGRESS_SHARE = 30;
106
+ var preProcessAudio = function (audioBuffer, sampleRate, fps, framesCount, onPreProcessProgress) { return __awaiter(void 0, void 0, void 0, function () {
107
+ var t, audioDataStep, processingBuffer, skipFramesCount, processSpectrum, detectBeat, spectrums, beatFrameIndices, beatEvents, nextPreProcessMilestone, i, currentFrameData, audioDataParser, spectrum, beat, p;
108
+ var _a, _b;
109
+ return __generator(this, function (_c) {
110
+ switch (_c.label) {
111
+ case 0:
112
+ if (framesCount === 0) {
113
+ for (t = 1; t <= PRE_PROCESS_PROGRESS_SHARE; t += 1) {
114
+ (_a = onPreProcessProgress) === null || _a === void 0 ? void 0 : _a(t);
115
+ }
116
+ return [2 /*return*/, { spectrums: [], beatFrameIndices: [], beatEvents: [] }];
117
+ }
118
+ audioDataStep = Math.trunc(audioBuffer.length / framesCount);
119
+ processingBuffer = new Float32Array(PROCESSING_BUFFER_SIZE).fill(0);
120
+ skipFramesCount = fps < 45 ? 1 : 2;
121
+ processSpectrum = audio_1.createSpectrumsProcessor(sampleRate, skipFramesCount);
122
+ detectBeat = beats_1.createBeatDetector(fps);
123
+ spectrums = [];
124
+ beatFrameIndices = [];
125
+ beatEvents = [];
126
+ nextPreProcessMilestone = 1;
127
+ i = 0;
128
+ _c.label = 1;
129
+ case 1:
130
+ if (!(i < framesCount)) return [3 /*break*/, 4];
131
+ currentFrameData = exports.PCM_FORMAT.parseFunction(audioBuffer, i * audioDataStep, i * audioDataStep + audioDataStep);
132
+ processingBuffer.copyWithin(0, currentFrameData.length);
133
+ processingBuffer.set(currentFrameData, PROCESSING_BUFFER_SIZE - currentFrameData.length);
134
+ audioDataParser = function () { return Array.from(processingBuffer); };
135
+ spectrum = processSpectrum(audioDataParser);
136
+ beat = detectBeat(spectrum);
137
+ spectrums.push(spectrum);
138
+ if (beat.isBeat) {
139
+ beatFrameIndices.push(i);
140
+ beatEvents.push({ frameIndex: i, intensity: beat.intensity });
141
+ }
142
+ p = PRE_PROCESS_PROGRESS_SHARE * (i + 1) / framesCount;
143
+ while (nextPreProcessMilestone <= PRE_PROCESS_PROGRESS_SHARE
144
+ && p >= nextPreProcessMilestone) {
145
+ (_b = onPreProcessProgress) === null || _b === void 0 ? void 0 : _b(nextPreProcessMilestone);
146
+ nextPreProcessMilestone += 1;
147
+ }
148
+ return [4 /*yield*/, waitForEventLoop_1.waitForEventLoop()];
149
+ case 2:
150
+ _c.sent();
151
+ _c.label = 3;
152
+ case 3:
153
+ i++;
154
+ return [3 /*break*/, 1];
155
+ case 4: return [2 /*return*/, { spectrums: spectrums, beatFrameIndices: beatFrameIndices, beatEvents: beatEvents }];
156
+ }
157
+ });
158
+ }); };
159
+ function prepareBackgroundForRender(params) {
160
+ var _a, _b, _c, _d, _e, _f;
161
+ return __awaiter(this, void 0, void 0, function () {
162
+ var useVideoBackground, backgroundVideoPath, backgroundImagePath, beatFrameIndices, framesCount, outputResolution, fps, autoEditVideo, videoInfo, sceneChanges, _g, backgroundWidth_1, backgroundHeight_1, videoFrameSize, encodeVideoFrame, segments, concatFilePath, videoFrameReader, firstFrame, defaultColor_1, staticBackgroundBuffer_1, backgroundImageBmpBuffer, backgroundImage, backgroundWidth, backgroundHeight, defaultColor, bpmEncoder, staticBackgroundBuffer;
163
+ return __generator(this, function (_h) {
164
+ switch (_h.label) {
165
+ case 0:
166
+ useVideoBackground = params.useVideoBackground, backgroundVideoPath = params.backgroundVideoPath, backgroundImagePath = params.backgroundImagePath, beatFrameIndices = params.beatFrameIndices, framesCount = params.framesCount, outputResolution = params.outputResolution, fps = params.fps, autoEditVideo = params.autoEditVideo;
167
+ if (!(useVideoBackground && backgroundVideoPath)) return [3 /*break*/, 6];
168
+ return [4 /*yield*/, video_1.getVideoInfo(backgroundVideoPath)];
169
+ case 1:
170
+ videoInfo = _h.sent();
171
+ if (!autoEditVideo) return [3 /*break*/, 3];
172
+ return [4 /*yield*/, video_1.detectSceneChanges(backgroundVideoPath)];
173
+ case 2:
174
+ _g = _h.sent();
175
+ return [3 /*break*/, 4];
176
+ case 3:
177
+ _g = [];
178
+ _h.label = 4;
179
+ case 4:
180
+ sceneChanges = _g;
181
+ backgroundWidth_1 = (_b = (_a = outputResolution) === null || _a === void 0 ? void 0 : _a.width, (_b !== null && _b !== void 0 ? _b : videoInfo.width));
182
+ backgroundHeight_1 = (_d = (_c = outputResolution) === null || _c === void 0 ? void 0 : _c.height, (_d !== null && _d !== void 0 ? _d : videoInfo.height));
183
+ videoFrameSize = backgroundWidth_1 * backgroundHeight_1 * 3;
184
+ encodeVideoFrame = bpmEncoder_1.createBgrFrameEncoder({ width: backgroundWidth_1, height: backgroundHeight_1 });
185
+ segments = video_1.buildBeatSyncedSegments(beatFrameIndices, framesCount, sceneChanges, videoInfo.duration);
186
+ concatFilePath = video_1.writeConcatFile(segments, backgroundVideoPath, videoInfo.duration, fps);
187
+ videoFrameReader = video_1.spawnConcatVideoFrameReader(__assign({ concatFilePath: concatFilePath,
188
+ fps: fps, totalFrames: framesCount }, (outputResolution && {
189
+ width: backgroundWidth_1,
190
+ height: backgroundHeight_1,
191
+ sourceWidth: videoInfo.width,
192
+ sourceHeight: videoInfo.height,
193
+ })));
194
+ return [4 /*yield*/, video_1.readVideoFrame(videoFrameReader.stdout, videoFrameSize)];
195
+ case 5:
196
+ firstFrame = _h.sent();
197
+ if (!firstFrame) {
198
+ throw new Error("Could not read frames from video: " + backgroundVideoPath);
199
+ }
200
+ defaultColor_1 = image_1.getVideoFrameColor(firstFrame, backgroundWidth_1, backgroundHeight_1);
201
+ staticBackgroundBuffer_1 = encodeVideoFrame(firstFrame);
202
+ return [2 /*return*/, {
203
+ backgroundWidth: backgroundWidth_1,
204
+ backgroundHeight: backgroundHeight_1,
205
+ defaultColor: defaultColor_1,
206
+ staticBackgroundBuffer: staticBackgroundBuffer_1,
207
+ videoFrameReader: videoFrameReader,
208
+ videoFrameSize: videoFrameSize,
209
+ encodeVideoFrame: encodeVideoFrame,
210
+ concatFilePath: concatFilePath,
211
+ }];
212
+ case 6:
213
+ if (!backgroundImagePath) {
214
+ throw new Error('Background image path is required when not using video background.');
215
+ }
216
+ return [4 /*yield*/, image_1.convertToBmp(backgroundImagePath, (_e = outputResolution) === null || _e === void 0 ? void 0 : _e.width, (_f = outputResolution) === null || _f === void 0 ? void 0 : _f.height)];
217
+ case 7:
218
+ backgroundImageBmpBuffer = _h.sent();
219
+ backgroundImage = image_1.parseImage(backgroundImageBmpBuffer);
220
+ backgroundWidth = backgroundImage.width;
221
+ backgroundHeight = backgroundImage.height;
222
+ defaultColor = image_1.getImageColor(backgroundImage);
223
+ bpmEncoder = bpmEncoder_1.createBpmEncoder({ width: backgroundWidth, height: backgroundHeight });
224
+ staticBackgroundBuffer = bpmEncoder(backgroundImage.data);
225
+ return [2 /*return*/, {
226
+ backgroundWidth: backgroundWidth,
227
+ backgroundHeight: backgroundHeight,
228
+ defaultColor: defaultColor,
229
+ staticBackgroundBuffer: staticBackgroundBuffer,
230
+ videoFrameSize: 0,
231
+ }];
232
+ }
233
+ });
234
+ });
235
+ }
236
+ function resolveBackgroundFrameBuffer(params) {
237
+ return __awaiter(this, void 0, void 0, function () {
238
+ var frameIndex, useVideoBackground, videoFrameReader, videoFrameSize, encodeVideoFrame, staticBackgroundBuffer, videoFrame;
239
+ return __generator(this, function (_a) {
240
+ switch (_a.label) {
241
+ case 0:
242
+ frameIndex = params.frameIndex, useVideoBackground = params.useVideoBackground, videoFrameReader = params.videoFrameReader, videoFrameSize = params.videoFrameSize, encodeVideoFrame = params.encodeVideoFrame, staticBackgroundBuffer = params.staticBackgroundBuffer;
243
+ if (!(useVideoBackground && videoFrameReader && encodeVideoFrame)) return [3 /*break*/, 2];
244
+ if (frameIndex === 0) {
245
+ return [2 /*return*/, staticBackgroundBuffer];
246
+ }
247
+ return [4 /*yield*/, video_1.readVideoFrame(videoFrameReader.stdout, videoFrameSize)];
248
+ case 1:
249
+ videoFrame = _a.sent();
250
+ return [2 /*return*/, videoFrame
251
+ ? encodeVideoFrame(videoFrame)
252
+ : staticBackgroundBuffer];
253
+ case 2: return [2 /*return*/, staticBackgroundBuffer];
254
+ }
255
+ });
256
+ });
257
+ }
258
+ exports.renderAudioVisualizer = function (config, onProgress, shouldStop) {
259
+ return new Promise(function (resolve) { return __awaiter(void 0, void 0, void 0, function () {
260
+ var audioFilePath, outVideoPath, backgroundVideoPath, backgroundImagePath, useVideoBackground, audioReader, audioBuffer, sampleRate, spectrumBusMargin, FPS, ffmpeg_cfr, ffmpeg_preset, frame_processing_delay, audioDuration, framesCount, outputResolution, maxProgressReported, reportProgress, preprocessed, autoHighlight, spectrumsForRender, beatIndicesForRender, framesCountForRender, highlightSlice, separateHighlightFiles, passes, totalPassFrames, totalRenderMilestones, renderMilestonesCompleted, reportRenderProgress, lastExitCode, outputVideoFiles, _loop_1, _i, passes_1, pass, state_1;
261
+ return __generator(this, function (_a) {
262
+ switch (_a.label) {
263
+ case 0:
264
+ if (config.outVideo.spectrum && config.outVideo.polar) {
265
+ throw new Error('Cannot use both "spectrum" and "polar" options. Please specify only one visualizer type.');
266
+ }
267
+ if (!config.image && !config.video) {
268
+ throw new Error('Either "image" or "video" must be specified as the background source.');
269
+ }
270
+ if (config.image && config.video) {
271
+ throw new Error('Cannot use both "image" and "video" options. Please specify only one background source.');
272
+ }
273
+ audioFilePath = config_1.getAudioFilePath(config);
274
+ outVideoPath = config_1.getOutVideoPath(config);
275
+ backgroundVideoPath = config_1.getBackgroundVideoPath(config);
276
+ backgroundImagePath = config_1.getBackgroundImagePath(config);
277
+ useVideoBackground = !!backgroundVideoPath;
278
+ return [4 /*yield*/, audio_1.createAudioBuffer(audioFilePath, FFMPEG_FORMAT)];
279
+ case 1:
280
+ audioReader = _a.sent();
281
+ audioBuffer = audioReader.audioBuffer;
282
+ sampleRate = audioReader.sampleRate;
283
+ if (!sampleRate) {
284
+ throw new Error('ffmpeg didn\'t show audio sample rate');
285
+ }
286
+ spectrumBusMargin = config_1.getSpectrumBusMargin();
287
+ FPS = config_1.getFPS(config);
288
+ ffmpeg_cfr = config_1.getFfmpeg_cfr(config);
289
+ ffmpeg_preset = config_1.getFfmpeg_preset(config);
290
+ frame_processing_delay = config_1.getFrame_processing_delay(config);
291
+ audioDuration = audioBuffer.length / sampleRate;
292
+ framesCount = Math.trunc(audioDuration * FPS);
293
+ outputResolution = config_1.getOutputResolution(config);
294
+ maxProgressReported = -1;
295
+ reportProgress = function (progress) {
296
+ if (!onProgress) {
297
+ return;
298
+ }
299
+ var normalized = +Math.min(100, Math.max(0, progress)).toFixed(2);
300
+ if (normalized <= maxProgressReported) {
301
+ return;
302
+ }
303
+ maxProgressReported = normalized;
304
+ onProgress(normalized);
305
+ };
306
+ reportProgress(0);
307
+ return [4 /*yield*/, preProcessAudio(audioBuffer, sampleRate, FPS, framesCount, function (progress) {
308
+ reportProgress(progress);
309
+ })];
310
+ case 2:
311
+ preprocessed = _a.sent();
312
+ reportProgress(PRE_PROCESS_PROGRESS_SHARE);
313
+ autoHighlight = config_1.getAudioAutoHighlight(config);
314
+ spectrumsForRender = preprocessed.spectrums;
315
+ beatIndicesForRender = preprocessed.beatFrameIndices;
316
+ framesCountForRender = framesCount;
317
+ if (!autoHighlight) return [3 /*break*/, 4];
318
+ reportProgress(PRE_PROCESS_PROGRESS_SHARE + 1);
319
+ return [4 /*yield*/, highlight_1.computeHighlightSlice(FPS, framesCount, preprocessed.spectrums, preprocessed.beatEvents, config_1.getAudioAutoHighlightCount(config))];
320
+ case 3:
321
+ highlightSlice = _a.sent();
322
+ spectrumsForRender = highlightSlice.spectrums;
323
+ beatIndicesForRender = highlightSlice.beatFrameIndices;
324
+ framesCountForRender = highlightSlice.highlightFrames;
325
+ reportProgress(PRE_PROCESS_PROGRESS_SHARE + POST_AUDIO_PROGRESS_SHARE);
326
+ return [3 /*break*/, 5];
327
+ case 4:
328
+ reportProgress(PRE_PROCESS_PROGRESS_SHARE + POST_AUDIO_PROGRESS_SHARE);
329
+ _a.label = 5;
330
+ case 5:
331
+ separateHighlightFiles = autoHighlight &&
332
+ highlightSlice &&
333
+ highlightSlice.runs.length > 1;
334
+ passes = separateHighlightFiles && highlightSlice
335
+ ? highlightSlice.runs.map(function (run, i) {
336
+ var dir = path_1.default.dirname(outVideoPath);
337
+ var ext = path_1.default.extname(outVideoPath);
338
+ var base = path_1.default.basename(outVideoPath, ext);
339
+ var numberedPath = path_1.default.resolve(dir, base + "-" + (i + 1) + ext);
340
+ return {
341
+ outPath: numberedPath,
342
+ spectrums: run.spectrums,
343
+ beatIndices: run.beatFrameIndices,
344
+ frameCount: run.highlightFrames,
345
+ audioSegment: run.audioSegment,
346
+ };
347
+ })
348
+ : [
349
+ {
350
+ outPath: outVideoPath,
351
+ spectrums: spectrumsForRender,
352
+ beatIndices: beatIndicesForRender,
353
+ frameCount: framesCountForRender,
354
+ audioSegment: autoHighlight &&
355
+ highlightSlice &&
356
+ highlightSlice.audioSegments.length > 0
357
+ ? highlightSlice.audioSegments[0]
358
+ : undefined,
359
+ },
360
+ ];
361
+ totalPassFrames = passes.reduce(function (sum, pass) { return sum + pass.frameCount; }, 0);
362
+ totalRenderMilestones = totalPassFrames + (passes.length * 2);
363
+ renderMilestonesCompleted = 0;
364
+ reportRenderProgress = function (milestoneIncrement) {
365
+ if (milestoneIncrement === void 0) { milestoneIncrement = 1; }
366
+ renderMilestonesCompleted += milestoneIncrement;
367
+ reportProgress(PRE_PROCESS_PROGRESS_SHARE
368
+ + POST_AUDIO_PROGRESS_SHARE
369
+ + (RENDER_PROGRESS_SHARE * renderMilestonesCompleted / Math.max(1, totalRenderMilestones)));
370
+ };
371
+ lastExitCode = 0;
372
+ outputVideoFiles = [];
373
+ _loop_1 = function (pass) {
374
+ var _a, backgroundWidth, backgroundHeight, defaultColor, staticBackgroundBuffer, videoFrameReader, videoFrameSize, encodeVideoFrame, concatFilePath, createVisualizerFrame, ffmpegVideoWriter, exitPromise, stoppedEarly, i, spectrum, backgroundImageBuffer, commonVisualizerFrameProps, frameImage, isFrameProcessed, isDrained;
375
+ return __generator(this, function (_b) {
376
+ switch (_b.label) {
377
+ case 0: return [4 /*yield*/, prepareBackgroundForRender({
378
+ useVideoBackground: useVideoBackground,
379
+ backgroundVideoPath: backgroundVideoPath,
380
+ backgroundImagePath: backgroundImagePath,
381
+ beatFrameIndices: pass.beatIndices,
382
+ framesCount: pass.frameCount,
383
+ outputResolution: outputResolution,
384
+ fps: FPS,
385
+ autoEditVideo: config_1.getAutoEditVideo(config),
386
+ })];
387
+ case 1:
388
+ _a = _b.sent(), backgroundWidth = _a.backgroundWidth, backgroundHeight = _a.backgroundHeight, defaultColor = _a.defaultColor, staticBackgroundBuffer = _a.staticBackgroundBuffer, videoFrameReader = _a.videoFrameReader, videoFrameSize = _a.videoFrameSize, encodeVideoFrame = _a.encodeVideoFrame, concatFilePath = _a.concatFilePath;
389
+ reportRenderProgress();
390
+ createVisualizerFrame = createVisualizerFrameGenerator(config, backgroundWidth, backgroundHeight, defaultColor, spectrumBusMargin);
391
+ ffmpegVideoWriter = video_1.spawnFfmpegVideoWriter(__assign(__assign(__assign({ audioFilename: audioFilePath, videoFileName: pass.outPath, fps: FPS }, (pass.audioSegment && { audioSegment: pass.audioSegment })), (ffmpeg_cfr && { crf: ffmpeg_cfr })), (ffmpeg_preset && { preset: ffmpeg_preset })));
392
+ exitPromise = new Promise(function (res) {
393
+ var settled = false;
394
+ ffmpegVideoWriter.on('exit', function (code) {
395
+ if (settled) {
396
+ return;
397
+ }
398
+ settled = true;
399
+ res((code !== null && code !== void 0 ? code : 0));
400
+ });
401
+ });
402
+ stoppedEarly = false;
403
+ i = 0;
404
+ _b.label = 2;
405
+ case 2:
406
+ if (!(i < pass.frameCount)) return [3 /*break*/, 10];
407
+ spectrum = pass.spectrums[i];
408
+ return [4 /*yield*/, resolveBackgroundFrameBuffer({
409
+ frameIndex: i,
410
+ useVideoBackground: useVideoBackground,
411
+ videoFrameReader: videoFrameReader,
412
+ videoFrameSize: videoFrameSize,
413
+ encodeVideoFrame: encodeVideoFrame,
414
+ staticBackgroundBuffer: staticBackgroundBuffer,
415
+ })];
416
+ case 3:
417
+ backgroundImageBuffer = _b.sent();
418
+ commonVisualizerFrameProps = {
419
+ backgroundImageBuffer: backgroundImageBuffer,
420
+ spectrum: spectrum,
421
+ };
422
+ frameImage = createVisualizerFrame(commonVisualizerFrameProps);
423
+ isFrameProcessed = ffmpegVideoWriter.stdin.write(frameImage.data);
424
+ if (!!isFrameProcessed) return [3 /*break*/, 5];
425
+ return [4 /*yield*/, video_1.waitDrain(ffmpegVideoWriter.stdin, ffmpegVideoWriter)];
426
+ case 4:
427
+ isDrained = _b.sent();
428
+ if (!isDrained) {
429
+ stoppedEarly = true;
430
+ return [3 /*break*/, 10];
431
+ }
432
+ _b.label = 5;
433
+ case 5:
434
+ if (shouldStop && shouldStop()) {
435
+ stoppedEarly = true;
436
+ return [3 /*break*/, 10];
437
+ }
438
+ if (!frame_processing_delay) return [3 /*break*/, 7];
439
+ return [4 /*yield*/, sleep(frame_processing_delay)];
440
+ case 6:
441
+ _b.sent();
442
+ _b.label = 7;
443
+ case 7: return [4 /*yield*/, waitForEventLoop_1.waitForEventLoop()];
444
+ case 8:
445
+ _b.sent();
446
+ reportRenderProgress();
447
+ _b.label = 9;
448
+ case 9:
449
+ i++;
450
+ return [3 /*break*/, 2];
451
+ case 10:
452
+ if (videoFrameReader) {
453
+ videoFrameReader.kill();
454
+ }
455
+ if (concatFilePath) {
456
+ video_1.cleanupConcatFile(concatFilePath);
457
+ }
458
+ ffmpegVideoWriter.stdin.end();
459
+ return [4 /*yield*/, exitPromise];
460
+ case 11:
461
+ lastExitCode = _b.sent();
462
+ reportRenderProgress();
463
+ if (lastExitCode === 0 && !stoppedEarly) {
464
+ outputVideoFiles.push(pass.outPath);
465
+ }
466
+ if (stoppedEarly || lastExitCode !== 0) {
467
+ return [2 /*return*/, "break-passLoop"];
468
+ }
469
+ return [2 /*return*/];
470
+ }
471
+ });
472
+ };
473
+ _i = 0, passes_1 = passes;
474
+ _a.label = 6;
475
+ case 6:
476
+ if (!(_i < passes_1.length)) return [3 /*break*/, 9];
477
+ pass = passes_1[_i];
478
+ return [5 /*yield**/, _loop_1(pass)];
479
+ case 7:
480
+ state_1 = _a.sent();
481
+ switch (state_1) {
482
+ case "break-passLoop": return [3 /*break*/, 9];
483
+ }
484
+ _a.label = 8;
485
+ case 8:
486
+ _i++;
487
+ return [3 /*break*/, 6];
488
+ case 9:
489
+ if (lastExitCode === 0 && outputVideoFiles.length === passes.length) {
490
+ reportProgress(100);
491
+ }
492
+ resolve({ exitCode: lastExitCode, outputVideoFiles: outputVideoFiles });
493
+ return [2 /*return*/];
494
+ }
495
+ });
496
+ }); });
497
+ };
467
498
  //# sourceMappingURL=index.js.map