nodejs-audio-visualizer 5.5.2 → 5.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +84 -83
  3. package/dist/audio.d.ts +13 -13
  4. package/dist/audio.js +128 -128
  5. package/dist/beats.d.ts +39 -44
  6. package/dist/beats.js +220 -313
  7. package/dist/beats.js.map +1 -1
  8. package/dist/bpmEncoder.d.ts +14 -14
  9. package/dist/bpmEncoder.js +97 -97
  10. package/dist/config.d.ts +81 -80
  11. package/dist/config.js +392 -391
  12. package/dist/config.js.map +1 -1
  13. package/dist/dsp.d.ts +16 -16
  14. package/dist/dsp.js +76 -76
  15. package/dist/highlight.d.ts +91 -91
  16. package/dist/highlight.js +606 -606
  17. package/dist/image.d.ts +107 -97
  18. package/dist/image.js +614 -545
  19. package/dist/image.js.map +1 -1
  20. package/dist/index.d.ts +98 -96
  21. package/dist/index.js +673 -667
  22. package/dist/index.js.map +1 -1
  23. package/dist/subtitleConvert.d.ts +3 -3
  24. package/dist/subtitleConvert.js +61 -61
  25. package/dist/video.d.ts +171 -171
  26. package/dist/video.js +743 -743
  27. package/dist/waitForEventLoop.d.ts +1 -1
  28. package/dist/waitForEventLoop.js +5 -5
  29. package/package.json +62 -61
  30. package/src/audio.ts +129 -129
  31. package/src/beats.ts +279 -396
  32. package/src/bpmEncoder.ts +97 -97
  33. package/src/config.ts +504 -501
  34. package/src/dsp.ts +95 -95
  35. package/src/highlight.ts +617 -617
  36. package/src/image.ts +791 -707
  37. package/src/index.ts +896 -885
  38. package/src/subtitleConvert.ts +63 -63
  39. package/src/tests/audio.test.ts +102 -102
  40. package/src/tests/beats.test.ts +134 -134
  41. package/src/tests/config.test.ts +568 -553
  42. package/src/tests/dsp.test.ts +24 -24
  43. package/src/tests/highlight.test.ts +397 -397
  44. package/src/tests/image.test.ts +371 -319
  45. package/src/tests/index.test.ts +180 -180
  46. package/src/tests/subtitleConvert.test.ts +23 -23
  47. package/src/typings.d.ts +28 -6
  48. package/src/waitForEventLoop.ts +2 -2
package/dist/index.js CHANGED
@@ -1,668 +1,674 @@
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 __spreadArrays = (this && this.__spreadArrays) || function () {
50
- for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
51
- for (var r = Array(s), k = 0, i = 0; i < il; i++)
52
- for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
53
- r[k] = a[j];
54
- return r;
55
- };
56
- var __importDefault = (this && this.__importDefault) || function (mod) {
57
- return (mod && mod.__esModule) ? mod : { "default": mod };
58
- };
59
- Object.defineProperty(exports, "__esModule", { value: true });
60
- var path_1 = __importDefault(require("path"));
61
- var fs_1 = require("fs");
62
- var config_1 = require("./config");
63
- var audio_1 = require("./audio");
64
- var image_1 = require("./image");
65
- var subtitleConvert_1 = require("./subtitleConvert");
66
- var video_1 = require("./video");
67
- var bpmEncoder_1 = require("./bpmEncoder");
68
- var beats_1 = require("./beats");
69
- var beats_2 = require("./beats");
70
- exports.estimateTempo = beats_2.estimateTempo;
71
- exports.beatGridFrameIndices = beats_2.beatGridFrameIndices;
72
- exports.shiftTempoPhase = beats_2.shiftTempoPhase;
73
- exports.tempoForWindow = beats_2.tempoForWindow;
74
- var highlight_1 = require("./highlight");
75
- var waitForEventLoop_1 = require("./waitForEventLoop");
76
- var highlight_2 = require("./highlight");
77
- exports.computeHighlightSlice = highlight_2.computeHighlightSlice;
78
- exports.HIGHLIGHT_DURATION_SEC = highlight_2.HIGHLIGHT_DURATION_SEC;
79
- exports.MAX_BEAT_SNAP_SEC = highlight_2.MAX_BEAT_SNAP_SEC;
80
- exports.PCM_FORMAT = {
81
- bit: 8,
82
- sign: 'u',
83
- parseFunction: audio_1.bufferToUInt8
84
- };
85
- var FFMPEG_FORMAT = "" + exports.PCM_FORMAT.sign + exports.PCM_FORMAT.bit;
86
- var PROCESSING_BUFFER_SIZE = Math.pow(2, 12);
87
- var sleep = function (timeout) {
88
- return new Promise(function (resolve) { return setTimeout(resolve, timeout); });
89
- };
90
- var createVisualizerFrameGenerator = function (config, backgroundWidth, backgroundHeight, defaultColor, spectrumBusMargin) {
91
- if (config.outVideo.spectrum) {
92
- var createSpectrumVisualizerFrame_1 = image_1.createSpectrumVisualizerFrameGenerator();
93
- var spectrumWidth_1 = config_1.getSpectrumWidthAbsolute(config, backgroundWidth);
94
- var spectrumHeight_1 = config_1.getSpectrumHeightAbsolute(config, backgroundHeight);
95
- var spectrumX_1 = config_1.getSpectrumXAbsolute(config, spectrumWidth_1, backgroundWidth);
96
- var spectrumY_1 = config_1.getSpectrumYAbsolute(config, spectrumHeight_1, backgroundHeight);
97
- var spectrumRotation_1 = config_1.getSpectrumRotation(config);
98
- var spectrumColor_1 = config_1.getSpectrumColor(config) || image_1.invertColor(defaultColor);
99
- var spectrumEffect_1 = config_1.getSpectrumEffect(config);
100
- var spectrumOpacity_1 = config_1.getSpectrumOpacityParsed(config);
101
- return function (params) {
102
- return createSpectrumVisualizerFrame_1(__assign(__assign({}, params), { size: { width: spectrumWidth_1, height: spectrumHeight_1 }, position: { x: spectrumX_1, y: spectrumY_1 }, rotation: spectrumRotation_1, margin: spectrumBusMargin, color: spectrumColor_1, opacity: spectrumOpacity_1, spectrumEffect: spectrumEffect_1 }));
103
- };
104
- }
105
- if (config.outVideo.polar) {
106
- var createPolarVisualizerFrame_1 = image_1.createPolarVisualizerFrameGenerator();
107
- var polarX_1 = config_1.getPolarXAbsolute(config, backgroundWidth);
108
- var polarY_1 = config_1.getPolarYAbsolute(config, backgroundHeight);
109
- var polarInnerRadius_1 = config_1.getPolarInnerRadius(config);
110
- var polarMaxBarLength_1 = config_1.getPolarMaxBarLength(config);
111
- var polarBarWidth_1 = config_1.getPolarBarWidth(config);
112
- var polarColor_1 = config_1.getPolarColor(config) || image_1.invertColor(defaultColor);
113
- var polarEffect_1 = config_1.getPolarEffect(config);
114
- var polarOpacity_1 = config_1.getPolarOpacityParsed(config);
115
- return function (params) {
116
- 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 }));
117
- };
118
- }
119
- return function (params) { return params.backgroundImageBuffer; };
120
- };
121
- var PRE_PROCESS_PROGRESS_SHARE = 60;
122
- var POST_AUDIO_PROGRESS_SHARE = 10;
123
- var RENDER_PROGRESS_SHARE = 30;
124
- var preProcessAudio = function (audioBuffer, sampleRate, fps, framesCount, onPreProcessProgress) { return __awaiter(void 0, void 0, void 0, function () {
125
- var t, audioDataStep, processingBuffer, skipFramesCount, processSpectrum, detectBeat, spectrums, beatFrameIndices, beatEvents, nextPreProcessMilestone, i, currentFrameData, frameDataLength, frameDataTailStart, frameDataToProcess, audioDataParser, spectrum, beat, p;
126
- var _a, _b;
127
- return __generator(this, function (_c) {
128
- switch (_c.label) {
129
- case 0:
130
- if (framesCount === 0) {
131
- for (t = 1; t <= PRE_PROCESS_PROGRESS_SHARE; t += 1) {
132
- (_a = onPreProcessProgress) === null || _a === void 0 ? void 0 : _a(t);
133
- }
134
- return [2 /*return*/, { spectrums: [], beatFrameIndices: [], beatEvents: [] }];
135
- }
136
- audioDataStep = Math.trunc(audioBuffer.length / framesCount);
137
- processingBuffer = new Float32Array(PROCESSING_BUFFER_SIZE).fill(0);
138
- skipFramesCount = fps < 45 ? 1 : 2;
139
- processSpectrum = audio_1.createSpectrumsProcessor(sampleRate, skipFramesCount);
140
- detectBeat = beats_1.createBeatDetector(fps);
141
- spectrums = [];
142
- beatFrameIndices = [];
143
- beatEvents = [];
144
- nextPreProcessMilestone = 1;
145
- i = 0;
146
- _c.label = 1;
147
- case 1:
148
- if (!(i < framesCount)) return [3 /*break*/, 4];
149
- currentFrameData = exports.PCM_FORMAT.parseFunction(audioBuffer, i * audioDataStep, i * audioDataStep + audioDataStep);
150
- frameDataLength = Math.min(currentFrameData.length, PROCESSING_BUFFER_SIZE);
151
- frameDataTailStart = currentFrameData.length - frameDataLength;
152
- frameDataToProcess = currentFrameData.slice(frameDataTailStart);
153
- processingBuffer.copyWithin(0, frameDataLength);
154
- processingBuffer.set(frameDataToProcess, PROCESSING_BUFFER_SIZE - frameDataLength);
155
- audioDataParser = function () { return Array.from(processingBuffer); };
156
- spectrum = processSpectrum(audioDataParser);
157
- beat = detectBeat(spectrum);
158
- spectrums.push(spectrum);
159
- if (beat.isBeat) {
160
- beatFrameIndices.push(i);
161
- beatEvents.push({ frameIndex: i, intensity: beat.intensity });
162
- }
163
- p = PRE_PROCESS_PROGRESS_SHARE * (i + 1) / framesCount;
164
- while (nextPreProcessMilestone <= PRE_PROCESS_PROGRESS_SHARE
165
- && p >= nextPreProcessMilestone) {
166
- (_b = onPreProcessProgress) === null || _b === void 0 ? void 0 : _b(nextPreProcessMilestone);
167
- nextPreProcessMilestone += 1;
168
- }
169
- return [4 /*yield*/, waitForEventLoop_1.waitForEventLoop()];
170
- case 2:
171
- _c.sent();
172
- _c.label = 3;
173
- case 3:
174
- i++;
175
- return [3 /*break*/, 1];
176
- case 4: return [2 /*return*/, { spectrums: spectrums, beatFrameIndices: beatFrameIndices, beatEvents: beatEvents }];
177
- }
178
- });
179
- }); };
180
- function prepareBackgroundForRender(params) {
181
- var _a, _b, _c, _d, _e, _f, _g;
182
- return __awaiter(this, void 0, void 0, function () {
183
- var useVideoBackground, backgroundVideoPaths, backgroundImagePath, beatFrameIndices, beatIntensities, framesCount, outputResolution, fps, autoEditVideo, readVideoFrameTimeout, tempo, _h, startVideoIndex, hookVideoPath, hookFrameCount, videoInfos, primaryVideoInfo, backgroundWidth_1, backgroundHeight_1, videoFrameSize, encodeVideoFrame, hookVideoInfo, _j, hookAvailableFrames, _k, hookFrames_1, mainFramesCount, mainBeatFrameIndices_1, mainBeatIntensities_1, mainTempo, segments, sceneChanges, _l, videoSources, concatFilePath, cutFrameIndices, videoFrameReader, firstFrame, defaultColor_1, staticBackgroundBuffer_1, backgroundImageBmpBuffer, backgroundImage, backgroundWidth, backgroundHeight, defaultColor, bpmEncoder, staticBackgroundBuffer;
184
- return __generator(this, function (_m) {
185
- switch (_m.label) {
186
- case 0:
187
- useVideoBackground = params.useVideoBackground, backgroundVideoPaths = params.backgroundVideoPaths, backgroundImagePath = params.backgroundImagePath, beatFrameIndices = params.beatFrameIndices, beatIntensities = params.beatIntensities, framesCount = params.framesCount, outputResolution = params.outputResolution, fps = params.fps, autoEditVideo = params.autoEditVideo, readVideoFrameTimeout = params.readVideoFrameTimeout, tempo = params.tempo, _h = params.startVideoIndex, startVideoIndex = _h === void 0 ? 0 : _h, hookVideoPath = params.hookVideoPath, hookFrameCount = params.hookFrameCount;
188
- if (!(useVideoBackground && backgroundVideoPaths && backgroundVideoPaths.length > 0)) return [3 /*break*/, 15];
189
- return [4 /*yield*/, Promise.all(backgroundVideoPaths.map(function (videoPath) { return video_1.getVideoInfo(videoPath); }))];
190
- case 1:
191
- videoInfos = _m.sent();
192
- primaryVideoInfo = videoInfos[0];
193
- backgroundWidth_1 = (_b = (_a = outputResolution) === null || _a === void 0 ? void 0 : _a.width, (_b !== null && _b !== void 0 ? _b : primaryVideoInfo.width));
194
- backgroundHeight_1 = (_d = (_c = outputResolution) === null || _c === void 0 ? void 0 : _c.height, (_d !== null && _d !== void 0 ? _d : primaryVideoInfo.height));
195
- videoFrameSize = backgroundWidth_1 * backgroundHeight_1 * 3;
196
- encodeVideoFrame = bpmEncoder_1.createBgrFrameEncoder({ width: backgroundWidth_1, height: backgroundHeight_1 });
197
- if (!hookVideoPath) return [3 /*break*/, 3];
198
- return [4 /*yield*/, video_1.getVideoInfo(hookVideoPath)];
199
- case 2:
200
- _j = _m.sent();
201
- return [3 /*break*/, 4];
202
- case 3:
203
- _j = undefined;
204
- _m.label = 4;
205
- case 4:
206
- hookVideoInfo = _j;
207
- if (!hookVideoPath) return [3 /*break*/, 6];
208
- return [4 /*yield*/, video_1.getVideoFrameCount(hookVideoPath, fps)];
209
- case 5:
210
- _k = _m.sent();
211
- return [3 /*break*/, 7];
212
- case 6:
213
- _k = 0;
214
- _m.label = 7;
215
- case 7:
216
- hookAvailableFrames = _k;
217
- hookFrames_1 = !hookVideoInfo
218
- ? 0
219
- : hookFrameCount !== undefined
220
- ? Math.min(hookFrameCount, hookAvailableFrames, framesCount)
221
- : video_1.computeHookFrameCount(hookAvailableFrames / fps, fps, framesCount);
222
- mainFramesCount = framesCount - hookFrames_1;
223
- mainBeatFrameIndices_1 = [];
224
- mainBeatIntensities_1 = beatIntensities ? [] : undefined;
225
- beatFrameIndices.forEach(function (frameIndex, i) {
226
- var _a;
227
- if (frameIndex >= hookFrames_1) {
228
- mainBeatFrameIndices_1.push(frameIndex - hookFrames_1);
229
- (_a = mainBeatIntensities_1) === null || _a === void 0 ? void 0 : _a.push(beatIntensities[i]);
230
- }
231
- });
232
- mainTempo = tempo ? beats_1.shiftTempoPhase(tempo, hookFrames_1) : tempo;
233
- segments = void 0;
234
- if (!(mainFramesCount <= 0)) return [3 /*break*/, 8];
235
- segments = [];
236
- return [3 /*break*/, 13];
237
- case 8:
238
- if (!(backgroundVideoPaths.length > 1)) return [3 /*break*/, 9];
239
- segments = autoEditVideo
240
- ? video_1.buildShuffledVideoSegments(mainBeatFrameIndices_1, mainFramesCount, backgroundVideoPaths.length, fps, __assign({ beatIntensities: mainBeatIntensities_1 }, (mainTempo ? { tempo: mainTempo } : {})))
241
- : video_1.buildSequentialVideoSegments(mainFramesCount, videoInfos.map(function (info) { return info.duration; }), fps, startVideoIndex % backgroundVideoPaths.length);
242
- return [3 /*break*/, 13];
243
- case 9:
244
- if (!autoEditVideo) return [3 /*break*/, 11];
245
- return [4 /*yield*/, video_1.detectSceneChanges(backgroundVideoPaths[0])];
246
- case 10:
247
- _l = _m.sent();
248
- return [3 /*break*/, 12];
249
- case 11:
250
- _l = [];
251
- _m.label = 12;
252
- case 12:
253
- sceneChanges = _l;
254
- segments = video_1.buildBeatSyncedSegments(mainBeatFrameIndices_1, mainFramesCount, sceneChanges, primaryVideoInfo.duration, fps, __assign({ beatIntensities: mainBeatIntensities_1 }, (autoEditVideo && mainTempo ? { tempo: mainTempo } : {})));
255
- _m.label = 13;
256
- case 13:
257
- videoSources = videoInfos.map(function (info, i) { return ({ path: backgroundVideoPaths[i], duration: info.duration }); });
258
- if (hookVideoPath && hookVideoInfo && hookFrames_1 > 0) {
259
- segments = __spreadArrays([
260
- { outputStartFrame: 0, videoSeekSeconds: 0, frameCount: hookFrames_1, videoIndex: videoSources.length }
261
- ], segments.map(function (segment) { return (__assign(__assign({}, segment), { outputStartFrame: segment.outputStartFrame + hookFrames_1 })); }));
262
- videoSources.push({ path: hookVideoPath, duration: hookAvailableFrames / fps });
263
- }
264
- concatFilePath = video_1.writeConcatFile(segments, videoSources, fps);
265
- cutFrameIndices = autoEditVideo ? video_1.getCutFrameIndices(segments) : [];
266
- videoFrameReader = video_1.spawnConcatVideoFrameReader(__assign({ concatFilePath: concatFilePath,
267
- fps: fps, totalFrames: framesCount }, (outputResolution && {
268
- width: backgroundWidth_1,
269
- height: backgroundHeight_1,
270
- sourceWidth: primaryVideoInfo.width,
271
- sourceHeight: primaryVideoInfo.height,
272
- })));
273
- return [4 /*yield*/, video_1.readVideoFrame(videoFrameReader.stdout, videoFrameSize, readVideoFrameTimeout)];
274
- case 14:
275
- firstFrame = _m.sent();
276
- if (!firstFrame) {
277
- throw new Error("Could not read frames from video: " + videoSources.map(function (source) { return source.path; }).join(', '));
278
- }
279
- defaultColor_1 = image_1.getVideoFrameColor(firstFrame, backgroundWidth_1, backgroundHeight_1);
280
- staticBackgroundBuffer_1 = encodeVideoFrame(firstFrame);
281
- return [2 /*return*/, __assign({ backgroundWidth: backgroundWidth_1,
282
- backgroundHeight: backgroundHeight_1,
283
- defaultColor: defaultColor_1,
284
- staticBackgroundBuffer: staticBackgroundBuffer_1,
285
- videoFrameReader: videoFrameReader,
286
- videoFrameSize: videoFrameSize,
287
- encodeVideoFrame: encodeVideoFrame,
288
- concatFilePath: concatFilePath,
289
- cutFrameIndices: cutFrameIndices }, (hookVideoPath && ((_e = hookVideoInfo) === null || _e === void 0 ? void 0 : _e.hasAudio) && hookFrames_1 > 0 && {
290
- hookAudio: { path: hookVideoPath, frameCount: hookFrames_1 },
291
- }))];
292
- case 15:
293
- if (!backgroundImagePath) {
294
- throw new Error('Background image path is required when not using video background.');
295
- }
296
- return [4 /*yield*/, image_1.convertToBmp(backgroundImagePath, (_f = outputResolution) === null || _f === void 0 ? void 0 : _f.width, (_g = outputResolution) === null || _g === void 0 ? void 0 : _g.height)];
297
- case 16:
298
- backgroundImageBmpBuffer = _m.sent();
299
- backgroundImage = image_1.parseImage(backgroundImageBmpBuffer);
300
- backgroundWidth = backgroundImage.width;
301
- backgroundHeight = backgroundImage.height;
302
- defaultColor = image_1.getImageColor(backgroundImage);
303
- bpmEncoder = bpmEncoder_1.createBpmEncoder({ width: backgroundWidth, height: backgroundHeight });
304
- staticBackgroundBuffer = bpmEncoder(backgroundImage.data);
305
- return [2 /*return*/, {
306
- backgroundWidth: backgroundWidth,
307
- backgroundHeight: backgroundHeight,
308
- defaultColor: defaultColor,
309
- staticBackgroundBuffer: staticBackgroundBuffer,
310
- videoFrameSize: 0,
311
- cutFrameIndices: [],
312
- }];
313
- }
314
- });
315
- });
316
- }
317
- function resolveBackgroundFrameBuffer(params) {
318
- return __awaiter(this, void 0, void 0, function () {
319
- var frameIndex, useVideoBackground, videoFrameReader, videoFrameSize, encodeVideoFrame, staticBackgroundBuffer, readVideoFrameTimeout, videoFrame;
320
- return __generator(this, function (_a) {
321
- switch (_a.label) {
322
- case 0:
323
- frameIndex = params.frameIndex, useVideoBackground = params.useVideoBackground, videoFrameReader = params.videoFrameReader, videoFrameSize = params.videoFrameSize, encodeVideoFrame = params.encodeVideoFrame, staticBackgroundBuffer = params.staticBackgroundBuffer, readVideoFrameTimeout = params.readVideoFrameTimeout;
324
- if (!(useVideoBackground && videoFrameReader && encodeVideoFrame)) return [3 /*break*/, 2];
325
- if (frameIndex === 0) {
326
- return [2 /*return*/, { frameBuffer: staticBackgroundBuffer, frameReadFailed: false }];
327
- }
328
- return [4 /*yield*/, video_1.readVideoFrame(videoFrameReader.stdout, videoFrameSize, readVideoFrameTimeout)];
329
- case 1:
330
- videoFrame = _a.sent();
331
- if (videoFrame) {
332
- return [2 /*return*/, { frameBuffer: encodeVideoFrame(videoFrame), frameReadFailed: false }];
333
- }
334
- return [2 /*return*/, { frameBuffer: staticBackgroundBuffer, frameReadFailed: false }];
335
- case 2: return [2 /*return*/, { frameBuffer: staticBackgroundBuffer, frameReadFailed: false }];
336
- }
337
- });
338
- });
339
- }
340
- exports.renderAudioVisualizer = function (config, onProgress, shouldStop) {
341
- return new Promise(function (resolve) { return __awaiter(void 0, void 0, void 0, function () {
342
- var audioFilePath, outVideoPath, subtitleSpec, subtitleFilePath, subtitleFileIsTemporary, subtitleAlignmentAss, absPath, ext, raw, backgroundVideoPaths, hookVideoPath, backgroundImagePath, useVideoBackground, audioReader, audioBuffer, sampleRate, spectrumBusMargin, FPS, ffmpeg_cfr, ffmpeg_preset, frame_processing_delay, videoTimeouts, audioDuration, framesCount, outputResolution, maxProgressReported, reportProgress, preprocessed, autoHighlight, autoEditVideo, trackTempo, sampleRateNum, maxSamples, tempoBuffer, spectrumsForRender, beatIndicesForRender, beatIntensitiesForRender, framesCountForRender, highlightSlice, hookLeadInFrames, _a, cameraShakeEnabled, shakeDelayFrames, separateHighlightFiles, passes, totalPassFrames, totalRenderMilestones, renderMilestonesCompleted, reportRenderProgress, lastExitCode, exitReason, outputVideoFiles, _loop_1, passIndex, state_1;
343
- var _b, _c, _d;
344
- return __generator(this, function (_e) {
345
- switch (_e.label) {
346
- case 0:
347
- if (config.outVideo.spectrum && config.outVideo.polar) {
348
- throw new Error('Cannot use both "spectrum" and "polar" options. Please specify only one visualizer type.');
349
- }
350
- if (!config.image && !config.video) {
351
- throw new Error('Either "image" or "video" must be specified as the background source.');
352
- }
353
- if (config.image && config.video) {
354
- throw new Error('Cannot use both "image" and "video" options. Please specify only one background source.');
355
- }
356
- audioFilePath = config_1.getAudioFilePath(config);
357
- outVideoPath = config_1.getOutVideoPath(config);
358
- subtitleSpec = config_1.getSubtitleRenderSpec(config);
359
- subtitleFileIsTemporary = false;
360
- subtitleAlignmentAss = 2;
361
- if (subtitleSpec) {
362
- subtitleAlignmentAss = config_1.subtitleAlignmentToAss(subtitleSpec.alignment);
363
- if (subtitleSpec.source.kind === 'file') {
364
- absPath = subtitleSpec.source.path;
365
- ext = path_1.default.extname(absPath).toLowerCase();
366
- if (ext === '.lrc') {
367
- raw = fs_1.readFileSync(absPath, 'utf-8');
368
- subtitleFilePath = video_1.writeSubtitlesFile(subtitleConvert_1.lrcToSrt(raw));
369
- subtitleFileIsTemporary = true;
370
- }
371
- else {
372
- subtitleFilePath = absPath;
373
- }
374
- }
375
- else {
376
- subtitleFilePath = video_1.writeSubtitlesFile(subtitleConvert_1.normalizeInlineSubtitlesToSrt(subtitleSpec.source.text));
377
- subtitleFileIsTemporary = true;
378
- }
379
- }
380
- backgroundVideoPaths = config_1.getBackgroundVideoPaths(config);
381
- hookVideoPath = config_1.getHookVideoPath(config);
382
- backgroundImagePath = config_1.getBackgroundImagePath(config);
383
- useVideoBackground = !!backgroundVideoPaths && backgroundVideoPaths.length > 0;
384
- return [4 /*yield*/, audio_1.createAudioBuffer(audioFilePath, FFMPEG_FORMAT)];
385
- case 1:
386
- audioReader = _e.sent();
387
- audioBuffer = audioReader.audioBuffer;
388
- sampleRate = audioReader.sampleRate;
389
- if (!sampleRate) {
390
- throw new Error('ffmpeg didn\'t show audio sample rate');
391
- }
392
- spectrumBusMargin = config_1.getSpectrumBusMargin();
393
- FPS = config_1.getFPS(config);
394
- ffmpeg_cfr = config_1.getFfmpeg_cfr(config);
395
- ffmpeg_preset = config_1.getFfmpeg_preset(config);
396
- frame_processing_delay = config_1.getFrame_processing_delay(config);
397
- videoTimeouts = config_1.getVideoTimeouts(config);
398
- audioDuration = audioBuffer.length / sampleRate;
399
- framesCount = Math.trunc(audioDuration * FPS);
400
- outputResolution = config_1.getOutputResolution(config);
401
- maxProgressReported = -1;
402
- reportProgress = function (progress) {
403
- if (!onProgress) {
404
- return;
405
- }
406
- var normalized = +Math.min(100, Math.max(0, progress)).toFixed(2);
407
- if (normalized <= maxProgressReported) {
408
- return;
409
- }
410
- maxProgressReported = normalized;
411
- onProgress(normalized);
412
- };
413
- reportProgress(0);
414
- return [4 /*yield*/, preProcessAudio(audioBuffer, sampleRate, FPS, framesCount, function (progress) {
415
- reportProgress(progress);
416
- })];
417
- case 2:
418
- preprocessed = _e.sent();
419
- reportProgress(PRE_PROCESS_PROGRESS_SHARE);
420
- autoHighlight = config_1.getAudioAutoHighlight(config);
421
- autoEditVideo = config_1.getAutoEditVideo(config);
422
- trackTempo = null;
423
- if (autoEditVideo || autoHighlight) {
424
- sampleRateNum = Number(sampleRate);
425
- maxSamples = Math.floor(sampleRateNum * beats_1.MAX_TEMPO_ANALYSIS_SECONDS);
426
- tempoBuffer = audioBuffer.length > maxSamples
427
- ? audioBuffer.slice(0, maxSamples)
428
- : audioBuffer;
429
- trackTempo = beats_1.estimateTempo(audio_1.pcmU8ToFloatSamples(tempoBuffer), sampleRateNum, FPS);
430
- }
431
- spectrumsForRender = preprocessed.spectrums;
432
- beatIndicesForRender = preprocessed.beatFrameIndices;
433
- beatIntensitiesForRender = preprocessed.beatEvents.map(function (event) { return event.intensity; });
434
- framesCountForRender = framesCount;
435
- if (!autoHighlight) return [3 /*break*/, 7];
436
- reportProgress(PRE_PROCESS_PROGRESS_SHARE + 1);
437
- if (!hookVideoPath) return [3 /*break*/, 4];
438
- return [4 /*yield*/, video_1.getVideoFrameCount(hookVideoPath, FPS)];
439
- case 3:
440
- _a = _e.sent();
441
- return [3 /*break*/, 5];
442
- case 4:
443
- _a = undefined;
444
- _e.label = 5;
445
- case 5:
446
- hookLeadInFrames = _a;
447
- return [4 /*yield*/, highlight_1.computeHighlightSlice(FPS, framesCount, preprocessed.spectrums, preprocessed.beatEvents, config_1.getAudioAutoHighlightCount(config), trackTempo, hookLeadInFrames)];
448
- case 6:
449
- highlightSlice = _e.sent();
450
- spectrumsForRender = highlightSlice.spectrums;
451
- beatIndicesForRender = highlightSlice.beatFrameIndices;
452
- beatIntensitiesForRender = highlightSlice.beatIntensities;
453
- framesCountForRender = highlightSlice.highlightFrames;
454
- reportProgress(PRE_PROCESS_PROGRESS_SHARE + POST_AUDIO_PROGRESS_SHARE);
455
- return [3 /*break*/, 8];
456
- case 7:
457
- reportProgress(PRE_PROCESS_PROGRESS_SHARE + POST_AUDIO_PROGRESS_SHARE);
458
- _e.label = 8;
459
- case 8:
460
- cameraShakeEnabled = config_1.getCameraShakeEnabled(config);
461
- shakeDelayFrames = Math.round(FPS * image_1.CAMERA_SHAKE_DELAY_SECONDS);
462
- separateHighlightFiles = autoHighlight &&
463
- highlightSlice &&
464
- highlightSlice.runs.length > 1;
465
- passes = separateHighlightFiles && highlightSlice
466
- ? highlightSlice.runs.map(function (run, i) {
467
- var dir = path_1.default.dirname(outVideoPath);
468
- var ext = path_1.default.extname(outVideoPath);
469
- var base = path_1.default.basename(outVideoPath, ext);
470
- var numberedPath = path_1.default.resolve(dir, base + "-" + (i + 1) + ext);
471
- return {
472
- outPath: numberedPath,
473
- spectrums: run.spectrums,
474
- beatIndices: run.beatFrameIndices,
475
- beatIntensities: run.beatIntensities,
476
- frameCount: run.highlightFrames,
477
- startFrame: run.startFrame,
478
- audioSegment: run.audioSegment,
479
- leadInFrames: run.leadInFrames,
480
- };
481
- })
482
- : [
483
- {
484
- outPath: outVideoPath,
485
- spectrums: spectrumsForRender,
486
- beatIndices: beatIndicesForRender,
487
- beatIntensities: beatIntensitiesForRender,
488
- frameCount: framesCountForRender,
489
- startFrame: (_c = (_b = highlightSlice) === null || _b === void 0 ? void 0 : _b.startFrame, (_c !== null && _c !== void 0 ? _c : 0)),
490
- audioSegment: autoHighlight &&
491
- highlightSlice &&
492
- highlightSlice.audioSegments.length > 0
493
- ? highlightSlice.audioSegments[0]
494
- : undefined,
495
- leadInFrames: ((_d = highlightSlice) === null || _d === void 0 ? void 0 : _d.runs.length) === 1 ? highlightSlice.runs[0].leadInFrames : undefined,
496
- },
497
- ];
498
- totalPassFrames = passes.reduce(function (sum, pass) { return sum + pass.frameCount; }, 0);
499
- totalRenderMilestones = totalPassFrames + (passes.length * 2);
500
- renderMilestonesCompleted = 0;
501
- reportRenderProgress = function (milestoneIncrement) {
502
- if (milestoneIncrement === void 0) { milestoneIncrement = 1; }
503
- renderMilestonesCompleted += milestoneIncrement;
504
- reportProgress(PRE_PROCESS_PROGRESS_SHARE
505
- + POST_AUDIO_PROGRESS_SHARE
506
- + (RENDER_PROGRESS_SHARE * renderMilestonesCompleted / Math.max(1, totalRenderMilestones)));
507
- };
508
- lastExitCode = 0;
509
- outputVideoFiles = [];
510
- _e.label = 9;
511
- case 9:
512
- _e.trys.push([9, , 14, 15]);
513
- _loop_1 = function (passIndex) {
514
- var pass, windowTempo, tempo, shakeAmplitudes, _a, backgroundWidth, backgroundHeight, defaultColor, staticBackgroundBuffer, videoFrameReader, videoFrameSize, encodeVideoFrame, concatFilePath, cutFrameIndices, hookAudio, beatShakeFrames, shakeFrames, createVisualizerFrame, ffmpegVideoWriter, exitPromise, stoppedEarly, i, spectrum, _b, backgroundImageBuffer, frameReadFailed, commonVisualizerFrameProps, frameImage, shakeOffset, isFrameProcessed, isDrained, _c, exitCode, ffmpegExitReason;
515
- return __generator(this, function (_d) {
516
- switch (_d.label) {
517
- case 0:
518
- pass = passes[passIndex];
519
- windowTempo = trackTempo
520
- ? beats_1.tempoForWindow(trackTempo, pass.startFrame, FPS, pass.frameCount, pass.beatIndices)
521
- : null;
522
- tempo = autoEditVideo ? windowTempo : null;
523
- shakeAmplitudes = tempo
524
- ? image_1.buildCutShakeAmplitudes(tempo.periodFrames)
525
- : undefined;
526
- return [4 /*yield*/, prepareBackgroundForRender(__assign(__assign(__assign({ useVideoBackground: useVideoBackground,
527
- backgroundVideoPaths: backgroundVideoPaths,
528
- backgroundImagePath: backgroundImagePath, beatFrameIndices: pass.beatIndices, beatIntensities: pass.beatIntensities, framesCount: pass.frameCount, outputResolution: outputResolution, fps: FPS, autoEditVideo: autoEditVideo, readVideoFrameTimeout: videoTimeouts.readVideoFrame }, (tempo ? { tempo: tempo } : {})), {
529
- // Each separate highlight output opens with a different video from the array.
530
- startVideoIndex: passIndex, hookVideoPath: hookVideoPath }), (pass.leadInFrames !== undefined ? { hookFrameCount: pass.leadInFrames } : {})))];
531
- case 1:
532
- _a = _d.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, cutFrameIndices = _a.cutFrameIndices, hookAudio = _a.hookAudio;
533
- reportRenderProgress();
534
- beatShakeFrames = function () { return (tempo
535
- ? beats_1.beatGridFrameIndices(tempo, pass.frameCount, image_1.CAMERA_SHAKE_EVERY_BEATS)
536
- : pass.beatIndices.filter(function (frameIndex) { return frameIndex > 0; }).filter(function (_, i) { return i % image_1.CAMERA_SHAKE_EVERY_BEATS === 0; })); };
537
- shakeFrames = new Set(autoEditVideo && cameraShakeEnabled
538
- ? (cutFrameIndices.length > 0 ? cutFrameIndices : beatShakeFrames())
539
- : []);
540
- createVisualizerFrame = createVisualizerFrameGenerator(config, backgroundWidth, backgroundHeight, defaultColor, spectrumBusMargin);
541
- ffmpegVideoWriter = video_1.spawnFfmpegVideoWriter(__assign(__assign(__assign(__assign(__assign(__assign(__assign({ audioFilename: audioFilePath, videoFileName: pass.outPath }, (subtitleFilePath && {
542
- subtitleFilename: subtitleFilePath,
543
- subtitleAlignmentAss: subtitleAlignmentAss,
544
- })), { fps: FPS }), (pass.audioSegment && { audioSegment: pass.audioSegment })), (hookAudio && {
545
- overlayAudio: { filename: hookAudio.path, durationSeconds: hookAudio.frameCount / FPS },
546
- })), (ffmpeg_cfr && { crf: ffmpeg_cfr })), (ffmpeg_preset && { preset: ffmpeg_preset })), { enhanceFilters: config_1.getEnhanceFilters(config) }));
547
- exitPromise = video_1.waitForProcessExit(ffmpegVideoWriter, videoTimeouts.waitForProcessExit);
548
- stoppedEarly = false;
549
- i = 0;
550
- _d.label = 2;
551
- case 2:
552
- if (!(i < pass.frameCount)) return [3 /*break*/, 10];
553
- spectrum = pass.spectrums[i];
554
- return [4 /*yield*/, resolveBackgroundFrameBuffer({
555
- frameIndex: i,
556
- useVideoBackground: useVideoBackground,
557
- videoFrameReader: videoFrameReader,
558
- videoFrameSize: videoFrameSize,
559
- encodeVideoFrame: encodeVideoFrame,
560
- staticBackgroundBuffer: staticBackgroundBuffer,
561
- readVideoFrameTimeout: videoTimeouts.readVideoFrame,
562
- })];
563
- case 3:
564
- _b = _d.sent(), backgroundImageBuffer = _b.frameBuffer, frameReadFailed = _b.frameReadFailed;
565
- commonVisualizerFrameProps = {
566
- backgroundImageBuffer: backgroundImageBuffer,
567
- spectrum: spectrum,
568
- };
569
- frameImage = createVisualizerFrame(commonVisualizerFrameProps);
570
- shakeOffset = image_1.getCutShakeOffset(i, shakeFrames, shakeAmplitudes, shakeDelayFrames);
571
- if (shakeOffset.x !== 0 || shakeOffset.y !== 0) {
572
- image_1.applyCameraShake(frameImage, backgroundWidth, backgroundHeight, shakeOffset.x, shakeOffset.y);
573
- }
574
- isFrameProcessed = ffmpegVideoWriter.stdin.write(frameImage.data);
575
- if (!!isFrameProcessed) return [3 /*break*/, 5];
576
- return [4 /*yield*/, video_1.waitDrain(ffmpegVideoWriter.stdin, ffmpegVideoWriter, videoTimeouts.waitDrain)];
577
- case 4:
578
- isDrained = _d.sent();
579
- if (!isDrained) {
580
- stoppedEarly = true;
581
- exitReason = 'ffmpeg stdin drain failed';
582
- return [3 /*break*/, 10];
583
- }
584
- _d.label = 5;
585
- case 5:
586
- if (shouldStop && shouldStop()) {
587
- stoppedEarly = true;
588
- exitReason = 'render stopped by shouldStop callback';
589
- return [3 /*break*/, 10];
590
- }
591
- if (!frame_processing_delay) return [3 /*break*/, 7];
592
- return [4 /*yield*/, sleep(frame_processing_delay)];
593
- case 6:
594
- _d.sent();
595
- _d.label = 7;
596
- case 7: return [4 /*yield*/, waitForEventLoop_1.waitForEventLoop()];
597
- case 8:
598
- _d.sent();
599
- reportRenderProgress();
600
- _d.label = 9;
601
- case 9:
602
- i++;
603
- return [3 /*break*/, 2];
604
- case 10:
605
- if (videoFrameReader) {
606
- videoFrameReader.kill();
607
- }
608
- if (concatFilePath) {
609
- video_1.cleanupConcatFile(concatFilePath);
610
- }
611
- ffmpegVideoWriter.stdin.end();
612
- return [4 /*yield*/, exitPromise];
613
- case 11:
614
- _c = _d.sent(), exitCode = _c.exitCode, ffmpegExitReason = _c.reason;
615
- lastExitCode = exitCode;
616
- if (!exitReason && ffmpegExitReason) {
617
- exitReason = ffmpegExitReason;
618
- }
619
- if (lastExitCode === 0 && !stoppedEarly) {
620
- reportRenderProgress();
621
- }
622
- if (lastExitCode === 0 && !stoppedEarly) {
623
- outputVideoFiles.push(pass.outPath);
624
- }
625
- if (stoppedEarly || lastExitCode !== 0) {
626
- if (exitReason && lastExitCode === 0) {
627
- lastExitCode = 1;
628
- }
629
- return [2 /*return*/, "break-passLoop"];
630
- }
631
- return [2 /*return*/];
632
- }
633
- });
634
- };
635
- passIndex = 0;
636
- _e.label = 10;
637
- case 10:
638
- if (!(passIndex < passes.length)) return [3 /*break*/, 13];
639
- return [5 /*yield**/, _loop_1(passIndex)];
640
- case 11:
641
- state_1 = _e.sent();
642
- switch (state_1) {
643
- case "break-passLoop": return [3 /*break*/, 13];
644
- }
645
- _e.label = 12;
646
- case 12:
647
- passIndex++;
648
- return [3 /*break*/, 10];
649
- case 13:
650
- if (lastExitCode === 0 && outputVideoFiles.length === passes.length) {
651
- reportProgress(100);
652
- }
653
- if (exitReason) {
654
- console.error(exitReason);
655
- }
656
- resolve({ exitCode: lastExitCode, reason: exitReason, outputVideoFiles: outputVideoFiles });
657
- return [3 /*break*/, 15];
658
- case 14:
659
- if (subtitleFilePath && subtitleFileIsTemporary) {
660
- video_1.cleanupTempFile(subtitleFilePath);
661
- }
662
- return [7 /*endfinally*/];
663
- case 15: return [2 /*return*/];
664
- }
665
- });
666
- }); });
667
- };
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 __spreadArrays = (this && this.__spreadArrays) || function () {
50
+ for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
51
+ for (var r = Array(s), k = 0, i = 0; i < il; i++)
52
+ for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
53
+ r[k] = a[j];
54
+ return r;
55
+ };
56
+ var __importDefault = (this && this.__importDefault) || function (mod) {
57
+ return (mod && mod.__esModule) ? mod : { "default": mod };
58
+ };
59
+ Object.defineProperty(exports, "__esModule", { value: true });
60
+ var path_1 = __importDefault(require("path"));
61
+ var fs_1 = require("fs");
62
+ var config_1 = require("./config");
63
+ var audio_1 = require("./audio");
64
+ var image_1 = require("./image");
65
+ var subtitleConvert_1 = require("./subtitleConvert");
66
+ var video_1 = require("./video");
67
+ var bpmEncoder_1 = require("./bpmEncoder");
68
+ var beats_1 = require("./beats");
69
+ var beats_2 = require("./beats");
70
+ exports.estimateTempo = beats_2.estimateTempo;
71
+ exports.beatGridFrameIndices = beats_2.beatGridFrameIndices;
72
+ exports.shiftTempoPhase = beats_2.shiftTempoPhase;
73
+ exports.tempoForWindow = beats_2.tempoForWindow;
74
+ var highlight_1 = require("./highlight");
75
+ var waitForEventLoop_1 = require("./waitForEventLoop");
76
+ var highlight_2 = require("./highlight");
77
+ exports.computeHighlightSlice = highlight_2.computeHighlightSlice;
78
+ exports.HIGHLIGHT_DURATION_SEC = highlight_2.HIGHLIGHT_DURATION_SEC;
79
+ exports.MAX_BEAT_SNAP_SEC = highlight_2.MAX_BEAT_SNAP_SEC;
80
+ exports.PCM_FORMAT = {
81
+ bit: 8,
82
+ sign: 'u',
83
+ parseFunction: audio_1.bufferToUInt8
84
+ };
85
+ var FFMPEG_FORMAT = "" + exports.PCM_FORMAT.sign + exports.PCM_FORMAT.bit;
86
+ var PROCESSING_BUFFER_SIZE = Math.pow(2, 12);
87
+ var sleep = function (timeout) {
88
+ return new Promise(function (resolve) { return setTimeout(resolve, timeout); });
89
+ };
90
+ var createVisualizerFrameGenerator = function (config, backgroundWidth, backgroundHeight, defaultColor, spectrumBusMargin) {
91
+ if (config.outVideo.spectrum) {
92
+ var createSpectrumVisualizerFrame_1 = image_1.createSpectrumVisualizerFrameGenerator();
93
+ var spectrumWidth_1 = config_1.getSpectrumWidthAbsolute(config, backgroundWidth);
94
+ var spectrumHeight_1 = config_1.getSpectrumHeightAbsolute(config, backgroundHeight);
95
+ var spectrumX_1 = config_1.getSpectrumXAbsolute(config, spectrumWidth_1, backgroundWidth);
96
+ var spectrumY_1 = config_1.getSpectrumYAbsolute(config, spectrumHeight_1, backgroundHeight);
97
+ var spectrumRotation_1 = config_1.getSpectrumRotation(config);
98
+ var spectrumColor_1 = config_1.getSpectrumColor(config) || image_1.invertColor(defaultColor);
99
+ var spectrumEffect_1 = config_1.getSpectrumEffect(config);
100
+ var spectrumOpacity_1 = config_1.getSpectrumOpacityParsed(config);
101
+ return function (params) {
102
+ return createSpectrumVisualizerFrame_1(__assign(__assign({}, params), { size: { width: spectrumWidth_1, height: spectrumHeight_1 }, position: { x: spectrumX_1, y: spectrumY_1 }, rotation: spectrumRotation_1, margin: spectrumBusMargin, color: spectrumColor_1, opacity: spectrumOpacity_1, spectrumEffect: spectrumEffect_1 }));
103
+ };
104
+ }
105
+ if (config.outVideo.polar) {
106
+ var createPolarVisualizerFrame_1 = image_1.createPolarVisualizerFrameGenerator();
107
+ var polarX_1 = config_1.getPolarXAbsolute(config, backgroundWidth);
108
+ var polarY_1 = config_1.getPolarYAbsolute(config, backgroundHeight);
109
+ var polarInnerRadius_1 = config_1.getPolarInnerRadius(config);
110
+ var polarMaxBarLength_1 = config_1.getPolarMaxBarLength(config);
111
+ var polarBarWidth_1 = config_1.getPolarBarWidth(config);
112
+ var polarColor_1 = config_1.getPolarColor(config) || image_1.invertColor(defaultColor);
113
+ var polarEffect_1 = config_1.getPolarEffect(config);
114
+ var polarOpacity_1 = config_1.getPolarOpacityParsed(config);
115
+ return function (params) {
116
+ 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 }));
117
+ };
118
+ }
119
+ return function (params) { return params.backgroundImageBuffer; };
120
+ };
121
+ var PRE_PROCESS_PROGRESS_SHARE = 60;
122
+ var POST_AUDIO_PROGRESS_SHARE = 10;
123
+ var RENDER_PROGRESS_SHARE = 30;
124
+ var preProcessAudio = function (audioBuffer, sampleRate, fps, framesCount, onPreProcessProgress) { return __awaiter(void 0, void 0, void 0, function () {
125
+ var t, audioDataStep, processingBuffer, skipFramesCount, processSpectrum, detectBeat, spectrums, beatFrameIndices, beatEvents, nextPreProcessMilestone, i, currentFrameData, frameDataLength, frameDataTailStart, frameDataToProcess, audioDataParser, spectrum, beat, p;
126
+ var _a, _b;
127
+ return __generator(this, function (_c) {
128
+ switch (_c.label) {
129
+ case 0:
130
+ if (framesCount === 0) {
131
+ for (t = 1; t <= PRE_PROCESS_PROGRESS_SHARE; t += 1) {
132
+ (_a = onPreProcessProgress) === null || _a === void 0 ? void 0 : _a(t);
133
+ }
134
+ return [2 /*return*/, { spectrums: [], beatFrameIndices: [], beatEvents: [] }];
135
+ }
136
+ audioDataStep = Math.trunc(audioBuffer.length / framesCount);
137
+ processingBuffer = new Float32Array(PROCESSING_BUFFER_SIZE).fill(0);
138
+ skipFramesCount = fps < 45 ? 1 : 2;
139
+ processSpectrum = audio_1.createSpectrumsProcessor(sampleRate, skipFramesCount);
140
+ detectBeat = beats_1.createBeatDetector(fps);
141
+ spectrums = [];
142
+ beatFrameIndices = [];
143
+ beatEvents = [];
144
+ nextPreProcessMilestone = 1;
145
+ i = 0;
146
+ _c.label = 1;
147
+ case 1:
148
+ if (!(i < framesCount)) return [3 /*break*/, 4];
149
+ currentFrameData = exports.PCM_FORMAT.parseFunction(audioBuffer, i * audioDataStep, i * audioDataStep + audioDataStep);
150
+ frameDataLength = Math.min(currentFrameData.length, PROCESSING_BUFFER_SIZE);
151
+ frameDataTailStart = currentFrameData.length - frameDataLength;
152
+ frameDataToProcess = currentFrameData.slice(frameDataTailStart);
153
+ processingBuffer.copyWithin(0, frameDataLength);
154
+ processingBuffer.set(frameDataToProcess, PROCESSING_BUFFER_SIZE - frameDataLength);
155
+ audioDataParser = function () { return Array.from(processingBuffer); };
156
+ spectrum = processSpectrum(audioDataParser);
157
+ beat = detectBeat(spectrum);
158
+ spectrums.push(spectrum);
159
+ if (beat.isBeat) {
160
+ beatFrameIndices.push(i);
161
+ beatEvents.push({ frameIndex: i, intensity: beat.intensity });
162
+ }
163
+ p = PRE_PROCESS_PROGRESS_SHARE * (i + 1) / framesCount;
164
+ while (nextPreProcessMilestone <= PRE_PROCESS_PROGRESS_SHARE
165
+ && p >= nextPreProcessMilestone) {
166
+ (_b = onPreProcessProgress) === null || _b === void 0 ? void 0 : _b(nextPreProcessMilestone);
167
+ nextPreProcessMilestone += 1;
168
+ }
169
+ return [4 /*yield*/, waitForEventLoop_1.waitForEventLoop()];
170
+ case 2:
171
+ _c.sent();
172
+ _c.label = 3;
173
+ case 3:
174
+ i++;
175
+ return [3 /*break*/, 1];
176
+ case 4: return [2 /*return*/, { spectrums: spectrums, beatFrameIndices: beatFrameIndices, beatEvents: beatEvents }];
177
+ }
178
+ });
179
+ }); };
180
+ function prepareBackgroundForRender(params) {
181
+ var _a, _b, _c, _d, _e, _f, _g;
182
+ return __awaiter(this, void 0, void 0, function () {
183
+ var useVideoBackground, backgroundVideoPaths, backgroundImagePath, beatFrameIndices, beatIntensities, framesCount, outputResolution, fps, autoEditVideo, readVideoFrameTimeout, tempo, _h, startVideoIndex, hookVideoPath, hookFrameCount, videoInfos, primaryVideoInfo, backgroundWidth_1, backgroundHeight_1, videoFrameSize, encodeVideoFrame, hookVideoInfo, _j, hookAvailableFrames, _k, hookFrames_1, mainFramesCount, mainBeatFrameIndices_1, mainBeatIntensities_1, mainTempo, segments, sceneChanges, _l, videoSources, concatFilePath, cutFrameIndices, videoFrameReader, firstFrame, defaultColor_1, staticBackgroundBuffer_1, backgroundImageBmpBuffer, backgroundImage, backgroundWidth, backgroundHeight, defaultColor, bpmEncoder, staticBackgroundBuffer;
184
+ return __generator(this, function (_m) {
185
+ switch (_m.label) {
186
+ case 0:
187
+ useVideoBackground = params.useVideoBackground, backgroundVideoPaths = params.backgroundVideoPaths, backgroundImagePath = params.backgroundImagePath, beatFrameIndices = params.beatFrameIndices, beatIntensities = params.beatIntensities, framesCount = params.framesCount, outputResolution = params.outputResolution, fps = params.fps, autoEditVideo = params.autoEditVideo, readVideoFrameTimeout = params.readVideoFrameTimeout, tempo = params.tempo, _h = params.startVideoIndex, startVideoIndex = _h === void 0 ? 0 : _h, hookVideoPath = params.hookVideoPath, hookFrameCount = params.hookFrameCount;
188
+ if (!(useVideoBackground && backgroundVideoPaths && backgroundVideoPaths.length > 0)) return [3 /*break*/, 15];
189
+ return [4 /*yield*/, Promise.all(backgroundVideoPaths.map(function (videoPath) { return video_1.getVideoInfo(videoPath); }))];
190
+ case 1:
191
+ videoInfos = _m.sent();
192
+ primaryVideoInfo = videoInfos[0];
193
+ backgroundWidth_1 = (_b = (_a = outputResolution) === null || _a === void 0 ? void 0 : _a.width, (_b !== null && _b !== void 0 ? _b : primaryVideoInfo.width));
194
+ backgroundHeight_1 = (_d = (_c = outputResolution) === null || _c === void 0 ? void 0 : _c.height, (_d !== null && _d !== void 0 ? _d : primaryVideoInfo.height));
195
+ videoFrameSize = backgroundWidth_1 * backgroundHeight_1 * 3;
196
+ encodeVideoFrame = bpmEncoder_1.createBgrFrameEncoder({ width: backgroundWidth_1, height: backgroundHeight_1 });
197
+ if (!hookVideoPath) return [3 /*break*/, 3];
198
+ return [4 /*yield*/, video_1.getVideoInfo(hookVideoPath)];
199
+ case 2:
200
+ _j = _m.sent();
201
+ return [3 /*break*/, 4];
202
+ case 3:
203
+ _j = undefined;
204
+ _m.label = 4;
205
+ case 4:
206
+ hookVideoInfo = _j;
207
+ if (!hookVideoPath) return [3 /*break*/, 6];
208
+ return [4 /*yield*/, video_1.getVideoFrameCount(hookVideoPath, fps)];
209
+ case 5:
210
+ _k = _m.sent();
211
+ return [3 /*break*/, 7];
212
+ case 6:
213
+ _k = 0;
214
+ _m.label = 7;
215
+ case 7:
216
+ hookAvailableFrames = _k;
217
+ hookFrames_1 = !hookVideoInfo
218
+ ? 0
219
+ : hookFrameCount !== undefined
220
+ ? Math.min(hookFrameCount, hookAvailableFrames, framesCount)
221
+ : video_1.computeHookFrameCount(hookAvailableFrames / fps, fps, framesCount);
222
+ mainFramesCount = framesCount - hookFrames_1;
223
+ mainBeatFrameIndices_1 = [];
224
+ mainBeatIntensities_1 = beatIntensities ? [] : undefined;
225
+ beatFrameIndices.forEach(function (frameIndex, i) {
226
+ var _a;
227
+ if (frameIndex >= hookFrames_1) {
228
+ mainBeatFrameIndices_1.push(frameIndex - hookFrames_1);
229
+ (_a = mainBeatIntensities_1) === null || _a === void 0 ? void 0 : _a.push(beatIntensities[i]);
230
+ }
231
+ });
232
+ mainTempo = tempo ? beats_1.shiftTempoPhase(tempo, hookFrames_1) : tempo;
233
+ segments = void 0;
234
+ if (!(mainFramesCount <= 0)) return [3 /*break*/, 8];
235
+ segments = [];
236
+ return [3 /*break*/, 13];
237
+ case 8:
238
+ if (!(backgroundVideoPaths.length > 1)) return [3 /*break*/, 9];
239
+ segments = autoEditVideo
240
+ ? video_1.buildShuffledVideoSegments(mainBeatFrameIndices_1, mainFramesCount, backgroundVideoPaths.length, fps, __assign({ beatIntensities: mainBeatIntensities_1 }, (mainTempo ? { tempo: mainTempo } : {})))
241
+ : video_1.buildSequentialVideoSegments(mainFramesCount, videoInfos.map(function (info) { return info.duration; }), fps, startVideoIndex % backgroundVideoPaths.length);
242
+ return [3 /*break*/, 13];
243
+ case 9:
244
+ if (!autoEditVideo) return [3 /*break*/, 11];
245
+ return [4 /*yield*/, video_1.detectSceneChanges(backgroundVideoPaths[0])];
246
+ case 10:
247
+ _l = _m.sent();
248
+ return [3 /*break*/, 12];
249
+ case 11:
250
+ _l = [];
251
+ _m.label = 12;
252
+ case 12:
253
+ sceneChanges = _l;
254
+ segments = video_1.buildBeatSyncedSegments(mainBeatFrameIndices_1, mainFramesCount, sceneChanges, primaryVideoInfo.duration, fps, __assign({ beatIntensities: mainBeatIntensities_1 }, (autoEditVideo && mainTempo ? { tempo: mainTempo } : {})));
255
+ _m.label = 13;
256
+ case 13:
257
+ videoSources = videoInfos.map(function (info, i) { return ({ path: backgroundVideoPaths[i], duration: info.duration }); });
258
+ if (hookVideoPath && hookVideoInfo && hookFrames_1 > 0) {
259
+ segments = __spreadArrays([
260
+ { outputStartFrame: 0, videoSeekSeconds: 0, frameCount: hookFrames_1, videoIndex: videoSources.length }
261
+ ], segments.map(function (segment) { return (__assign(__assign({}, segment), { outputStartFrame: segment.outputStartFrame + hookFrames_1 })); }));
262
+ videoSources.push({ path: hookVideoPath, duration: hookAvailableFrames / fps });
263
+ }
264
+ concatFilePath = video_1.writeConcatFile(segments, videoSources, fps);
265
+ cutFrameIndices = autoEditVideo ? video_1.getCutFrameIndices(segments) : [];
266
+ videoFrameReader = video_1.spawnConcatVideoFrameReader(__assign({ concatFilePath: concatFilePath,
267
+ fps: fps, totalFrames: framesCount }, (outputResolution && {
268
+ width: backgroundWidth_1,
269
+ height: backgroundHeight_1,
270
+ sourceWidth: primaryVideoInfo.width,
271
+ sourceHeight: primaryVideoInfo.height,
272
+ })));
273
+ return [4 /*yield*/, video_1.readVideoFrame(videoFrameReader.stdout, videoFrameSize, readVideoFrameTimeout)];
274
+ case 14:
275
+ firstFrame = _m.sent();
276
+ if (!firstFrame) {
277
+ throw new Error("Could not read frames from video: " + videoSources.map(function (source) { return source.path; }).join(', '));
278
+ }
279
+ defaultColor_1 = image_1.getVideoFrameColor(firstFrame, backgroundWidth_1, backgroundHeight_1);
280
+ staticBackgroundBuffer_1 = encodeVideoFrame(firstFrame);
281
+ return [2 /*return*/, __assign({ backgroundWidth: backgroundWidth_1,
282
+ backgroundHeight: backgroundHeight_1,
283
+ defaultColor: defaultColor_1,
284
+ staticBackgroundBuffer: staticBackgroundBuffer_1,
285
+ videoFrameReader: videoFrameReader,
286
+ videoFrameSize: videoFrameSize,
287
+ encodeVideoFrame: encodeVideoFrame,
288
+ concatFilePath: concatFilePath,
289
+ cutFrameIndices: cutFrameIndices }, (hookVideoPath && ((_e = hookVideoInfo) === null || _e === void 0 ? void 0 : _e.hasAudio) && hookFrames_1 > 0 && {
290
+ hookAudio: { path: hookVideoPath, frameCount: hookFrames_1 },
291
+ }))];
292
+ case 15:
293
+ if (!backgroundImagePath) {
294
+ throw new Error('Background image path is required when not using video background.');
295
+ }
296
+ return [4 /*yield*/, image_1.convertToBmp(backgroundImagePath, (_f = outputResolution) === null || _f === void 0 ? void 0 : _f.width, (_g = outputResolution) === null || _g === void 0 ? void 0 : _g.height)];
297
+ case 16:
298
+ backgroundImageBmpBuffer = _m.sent();
299
+ backgroundImage = image_1.parseImage(backgroundImageBmpBuffer);
300
+ backgroundWidth = backgroundImage.width;
301
+ backgroundHeight = backgroundImage.height;
302
+ defaultColor = image_1.getImageColor(backgroundImage);
303
+ bpmEncoder = bpmEncoder_1.createBpmEncoder({ width: backgroundWidth, height: backgroundHeight });
304
+ staticBackgroundBuffer = bpmEncoder(backgroundImage.data);
305
+ return [2 /*return*/, {
306
+ backgroundWidth: backgroundWidth,
307
+ backgroundHeight: backgroundHeight,
308
+ defaultColor: defaultColor,
309
+ staticBackgroundBuffer: staticBackgroundBuffer,
310
+ videoFrameSize: 0,
311
+ cutFrameIndices: [],
312
+ }];
313
+ }
314
+ });
315
+ });
316
+ }
317
+ function resolveBackgroundFrameBuffer(params) {
318
+ return __awaiter(this, void 0, void 0, function () {
319
+ var frameIndex, useVideoBackground, videoFrameReader, videoFrameSize, encodeVideoFrame, staticBackgroundBuffer, readVideoFrameTimeout, videoFrame;
320
+ return __generator(this, function (_a) {
321
+ switch (_a.label) {
322
+ case 0:
323
+ frameIndex = params.frameIndex, useVideoBackground = params.useVideoBackground, videoFrameReader = params.videoFrameReader, videoFrameSize = params.videoFrameSize, encodeVideoFrame = params.encodeVideoFrame, staticBackgroundBuffer = params.staticBackgroundBuffer, readVideoFrameTimeout = params.readVideoFrameTimeout;
324
+ if (!(useVideoBackground && videoFrameReader && encodeVideoFrame)) return [3 /*break*/, 2];
325
+ if (frameIndex === 0) {
326
+ return [2 /*return*/, { frameBuffer: staticBackgroundBuffer, frameReadFailed: false }];
327
+ }
328
+ return [4 /*yield*/, video_1.readVideoFrame(videoFrameReader.stdout, videoFrameSize, readVideoFrameTimeout)];
329
+ case 1:
330
+ videoFrame = _a.sent();
331
+ if (videoFrame) {
332
+ return [2 /*return*/, { frameBuffer: encodeVideoFrame(videoFrame), frameReadFailed: false }];
333
+ }
334
+ return [2 /*return*/, { frameBuffer: staticBackgroundBuffer, frameReadFailed: false }];
335
+ case 2: return [2 /*return*/, { frameBuffer: staticBackgroundBuffer, frameReadFailed: false }];
336
+ }
337
+ });
338
+ });
339
+ }
340
+ exports.renderAudioVisualizer = function (config, onProgress, shouldStop) {
341
+ return new Promise(function (resolve) { return __awaiter(void 0, void 0, void 0, function () {
342
+ var audioFilePath, outVideoPath, subtitleSpec, subtitleFilePath, subtitleFileIsTemporary, subtitleAlignmentAss, absPath, ext, raw, backgroundVideoPaths, hookVideoPath, backgroundImagePath, useVideoBackground, audioReader, audioBuffer, sampleRate, spectrumBusMargin, FPS, ffmpeg_cfr, ffmpeg_preset, frame_processing_delay, videoTimeouts, audioDuration, framesCount, outputResolution, maxProgressReported, reportProgress, preprocessed, autoHighlight, autoEditVideo, trackTempo, sampleRateNum, maxSamples, tempoBuffer, spectrumsForRender, beatIndicesForRender, beatIntensitiesForRender, framesCountForRender, highlightSlice, hookLeadInFrames, _a, cameraShakeEnabled, cutZoomEnabled, shakeDelayFrames, separateHighlightFiles, passes, totalPassFrames, totalRenderMilestones, renderMilestonesCompleted, reportRenderProgress, lastExitCode, exitReason, outputVideoFiles, _loop_1, passIndex, state_1;
343
+ var _b, _c, _d;
344
+ return __generator(this, function (_e) {
345
+ switch (_e.label) {
346
+ case 0:
347
+ if (config.outVideo.spectrum && config.outVideo.polar) {
348
+ throw new Error('Cannot use both "spectrum" and "polar" options. Please specify only one visualizer type.');
349
+ }
350
+ if (!config.image && !config.video) {
351
+ throw new Error('Either "image" or "video" must be specified as the background source.');
352
+ }
353
+ if (config.image && config.video) {
354
+ throw new Error('Cannot use both "image" and "video" options. Please specify only one background source.');
355
+ }
356
+ audioFilePath = config_1.getAudioFilePath(config);
357
+ outVideoPath = config_1.getOutVideoPath(config);
358
+ subtitleSpec = config_1.getSubtitleRenderSpec(config);
359
+ subtitleFileIsTemporary = false;
360
+ subtitleAlignmentAss = 2;
361
+ if (subtitleSpec) {
362
+ subtitleAlignmentAss = config_1.subtitleAlignmentToAss(subtitleSpec.alignment);
363
+ if (subtitleSpec.source.kind === 'file') {
364
+ absPath = subtitleSpec.source.path;
365
+ ext = path_1.default.extname(absPath).toLowerCase();
366
+ if (ext === '.lrc') {
367
+ raw = fs_1.readFileSync(absPath, 'utf-8');
368
+ subtitleFilePath = video_1.writeSubtitlesFile(subtitleConvert_1.lrcToSrt(raw));
369
+ subtitleFileIsTemporary = true;
370
+ }
371
+ else {
372
+ subtitleFilePath = absPath;
373
+ }
374
+ }
375
+ else {
376
+ subtitleFilePath = video_1.writeSubtitlesFile(subtitleConvert_1.normalizeInlineSubtitlesToSrt(subtitleSpec.source.text));
377
+ subtitleFileIsTemporary = true;
378
+ }
379
+ }
380
+ backgroundVideoPaths = config_1.getBackgroundVideoPaths(config);
381
+ hookVideoPath = config_1.getHookVideoPath(config);
382
+ backgroundImagePath = config_1.getBackgroundImagePath(config);
383
+ useVideoBackground = !!backgroundVideoPaths && backgroundVideoPaths.length > 0;
384
+ return [4 /*yield*/, audio_1.createAudioBuffer(audioFilePath, FFMPEG_FORMAT)];
385
+ case 1:
386
+ audioReader = _e.sent();
387
+ audioBuffer = audioReader.audioBuffer;
388
+ sampleRate = audioReader.sampleRate;
389
+ if (!sampleRate) {
390
+ throw new Error('ffmpeg didn\'t show audio sample rate');
391
+ }
392
+ spectrumBusMargin = config_1.getSpectrumBusMargin();
393
+ FPS = config_1.getFPS(config);
394
+ ffmpeg_cfr = config_1.getFfmpeg_cfr(config);
395
+ ffmpeg_preset = config_1.getFfmpeg_preset(config);
396
+ frame_processing_delay = config_1.getFrame_processing_delay(config);
397
+ videoTimeouts = config_1.getVideoTimeouts(config);
398
+ audioDuration = audioBuffer.length / sampleRate;
399
+ framesCount = Math.trunc(audioDuration * FPS);
400
+ outputResolution = config_1.getOutputResolution(config);
401
+ maxProgressReported = -1;
402
+ reportProgress = function (progress) {
403
+ if (!onProgress) {
404
+ return;
405
+ }
406
+ var normalized = +Math.min(100, Math.max(0, progress)).toFixed(2);
407
+ if (normalized <= maxProgressReported) {
408
+ return;
409
+ }
410
+ maxProgressReported = normalized;
411
+ onProgress(normalized);
412
+ };
413
+ reportProgress(0);
414
+ return [4 /*yield*/, preProcessAudio(audioBuffer, sampleRate, FPS, framesCount, function (progress) {
415
+ reportProgress(progress);
416
+ })];
417
+ case 2:
418
+ preprocessed = _e.sent();
419
+ reportProgress(PRE_PROCESS_PROGRESS_SHARE);
420
+ autoHighlight = config_1.getAudioAutoHighlight(config);
421
+ autoEditVideo = config_1.getAutoEditVideo(config);
422
+ trackTempo = null;
423
+ if (autoEditVideo || autoHighlight) {
424
+ sampleRateNum = Number(sampleRate);
425
+ maxSamples = Math.floor(sampleRateNum * beats_1.MAX_TEMPO_ANALYSIS_SECONDS);
426
+ tempoBuffer = audioBuffer.length > maxSamples
427
+ ? audioBuffer.slice(0, maxSamples)
428
+ : audioBuffer;
429
+ trackTempo = beats_1.estimateTempo(audio_1.pcmU8ToFloatSamples(tempoBuffer), sampleRateNum, FPS);
430
+ }
431
+ spectrumsForRender = preprocessed.spectrums;
432
+ beatIndicesForRender = preprocessed.beatFrameIndices;
433
+ beatIntensitiesForRender = preprocessed.beatEvents.map(function (event) { return event.intensity; });
434
+ framesCountForRender = framesCount;
435
+ if (!autoHighlight) return [3 /*break*/, 7];
436
+ reportProgress(PRE_PROCESS_PROGRESS_SHARE + 1);
437
+ if (!hookVideoPath) return [3 /*break*/, 4];
438
+ return [4 /*yield*/, video_1.getVideoFrameCount(hookVideoPath, FPS)];
439
+ case 3:
440
+ _a = _e.sent();
441
+ return [3 /*break*/, 5];
442
+ case 4:
443
+ _a = undefined;
444
+ _e.label = 5;
445
+ case 5:
446
+ hookLeadInFrames = _a;
447
+ return [4 /*yield*/, highlight_1.computeHighlightSlice(FPS, framesCount, preprocessed.spectrums, preprocessed.beatEvents, config_1.getAudioAutoHighlightCount(config), trackTempo, hookLeadInFrames)];
448
+ case 6:
449
+ highlightSlice = _e.sent();
450
+ spectrumsForRender = highlightSlice.spectrums;
451
+ beatIndicesForRender = highlightSlice.beatFrameIndices;
452
+ beatIntensitiesForRender = highlightSlice.beatIntensities;
453
+ framesCountForRender = highlightSlice.highlightFrames;
454
+ reportProgress(PRE_PROCESS_PROGRESS_SHARE + POST_AUDIO_PROGRESS_SHARE);
455
+ return [3 /*break*/, 8];
456
+ case 7:
457
+ reportProgress(PRE_PROCESS_PROGRESS_SHARE + POST_AUDIO_PROGRESS_SHARE);
458
+ _e.label = 8;
459
+ case 8:
460
+ cameraShakeEnabled = config_1.getCameraShakeEnabled(config);
461
+ cutZoomEnabled = config_1.getCutZoomEnabled(config);
462
+ shakeDelayFrames = Math.round(FPS * image_1.CAMERA_SHAKE_DELAY_SECONDS);
463
+ separateHighlightFiles = autoHighlight &&
464
+ highlightSlice &&
465
+ highlightSlice.runs.length > 1;
466
+ passes = separateHighlightFiles && highlightSlice
467
+ ? highlightSlice.runs.map(function (run, i) {
468
+ var dir = path_1.default.dirname(outVideoPath);
469
+ var ext = path_1.default.extname(outVideoPath);
470
+ var base = path_1.default.basename(outVideoPath, ext);
471
+ var numberedPath = path_1.default.resolve(dir, base + "-" + (i + 1) + ext);
472
+ return {
473
+ outPath: numberedPath,
474
+ spectrums: run.spectrums,
475
+ beatIndices: run.beatFrameIndices,
476
+ beatIntensities: run.beatIntensities,
477
+ frameCount: run.highlightFrames,
478
+ startFrame: run.startFrame,
479
+ audioSegment: run.audioSegment,
480
+ leadInFrames: run.leadInFrames,
481
+ };
482
+ })
483
+ : [
484
+ {
485
+ outPath: outVideoPath,
486
+ spectrums: spectrumsForRender,
487
+ beatIndices: beatIndicesForRender,
488
+ beatIntensities: beatIntensitiesForRender,
489
+ frameCount: framesCountForRender,
490
+ startFrame: (_c = (_b = highlightSlice) === null || _b === void 0 ? void 0 : _b.startFrame, (_c !== null && _c !== void 0 ? _c : 0)),
491
+ audioSegment: autoHighlight &&
492
+ highlightSlice &&
493
+ highlightSlice.audioSegments.length > 0
494
+ ? highlightSlice.audioSegments[0]
495
+ : undefined,
496
+ leadInFrames: ((_d = highlightSlice) === null || _d === void 0 ? void 0 : _d.runs.length) === 1 ? highlightSlice.runs[0].leadInFrames : undefined,
497
+ },
498
+ ];
499
+ totalPassFrames = passes.reduce(function (sum, pass) { return sum + pass.frameCount; }, 0);
500
+ totalRenderMilestones = totalPassFrames + (passes.length * 2);
501
+ renderMilestonesCompleted = 0;
502
+ reportRenderProgress = function (milestoneIncrement) {
503
+ if (milestoneIncrement === void 0) { milestoneIncrement = 1; }
504
+ renderMilestonesCompleted += milestoneIncrement;
505
+ reportProgress(PRE_PROCESS_PROGRESS_SHARE
506
+ + POST_AUDIO_PROGRESS_SHARE
507
+ + (RENDER_PROGRESS_SHARE * renderMilestonesCompleted / Math.max(1, totalRenderMilestones)));
508
+ };
509
+ lastExitCode = 0;
510
+ outputVideoFiles = [];
511
+ _e.label = 9;
512
+ case 9:
513
+ _e.trys.push([9, , 14, 15]);
514
+ _loop_1 = function (passIndex) {
515
+ var pass, windowTempo, tempo, shakeAmplitudes, zoomScales, _a, backgroundWidth, backgroundHeight, defaultColor, staticBackgroundBuffer, videoFrameReader, videoFrameSize, encodeVideoFrame, concatFilePath, cutFrameIndices, hookAudio, beatShakeFrames, effectFrames, shakeFrames, zoomFrames, createVisualizerFrame, ffmpegVideoWriter, exitPromise, stoppedEarly, i, spectrum, _b, backgroundImageBuffer, frameReadFailed, commonVisualizerFrameProps, frameImage, zoomScale, shakeOffset, isFrameProcessed, isDrained, _c, exitCode, ffmpegExitReason;
516
+ return __generator(this, function (_d) {
517
+ switch (_d.label) {
518
+ case 0:
519
+ pass = passes[passIndex];
520
+ windowTempo = trackTempo
521
+ ? beats_1.tempoForWindow(trackTempo, pass.startFrame, FPS, pass.frameCount, pass.beatIndices)
522
+ : null;
523
+ tempo = autoEditVideo ? windowTempo : null;
524
+ shakeAmplitudes = tempo
525
+ ? image_1.buildCutShakeAmplitudes(tempo.periodFrames)
526
+ : undefined;
527
+ zoomScales = image_1.buildCutZoomScales(tempo ? tempo.periodFrames : 0);
528
+ return [4 /*yield*/, prepareBackgroundForRender(__assign(__assign(__assign({ useVideoBackground: useVideoBackground,
529
+ backgroundVideoPaths: backgroundVideoPaths,
530
+ backgroundImagePath: backgroundImagePath, beatFrameIndices: pass.beatIndices, beatIntensities: pass.beatIntensities, framesCount: pass.frameCount, outputResolution: outputResolution, fps: FPS, autoEditVideo: autoEditVideo, readVideoFrameTimeout: videoTimeouts.readVideoFrame }, (tempo ? { tempo: tempo } : {})), {
531
+ // Each separate highlight output opens with a different video from the array.
532
+ startVideoIndex: passIndex, hookVideoPath: hookVideoPath }), (pass.leadInFrames !== undefined ? { hookFrameCount: pass.leadInFrames } : {})))];
533
+ case 1:
534
+ _a = _d.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, cutFrameIndices = _a.cutFrameIndices, hookAudio = _a.hookAudio;
535
+ reportRenderProgress();
536
+ beatShakeFrames = function () { return (tempo
537
+ ? beats_1.beatGridFrameIndices(tempo, pass.frameCount, image_1.CAMERA_SHAKE_EVERY_BEATS)
538
+ : pass.beatIndices.filter(function (frameIndex) { return frameIndex > 0; }).filter(function (_, i) { return i % image_1.CAMERA_SHAKE_EVERY_BEATS === 0; })); };
539
+ effectFrames = function () { return (cutFrameIndices.length > 0 ? cutFrameIndices : beatShakeFrames()); };
540
+ shakeFrames = new Set(autoEditVideo && cameraShakeEnabled ? effectFrames() : []);
541
+ zoomFrames = new Set(autoEditVideo && cutZoomEnabled ? effectFrames() : []);
542
+ createVisualizerFrame = createVisualizerFrameGenerator(config, backgroundWidth, backgroundHeight, defaultColor, spectrumBusMargin);
543
+ ffmpegVideoWriter = video_1.spawnFfmpegVideoWriter(__assign(__assign(__assign(__assign(__assign(__assign(__assign({ audioFilename: audioFilePath, videoFileName: pass.outPath }, (subtitleFilePath && {
544
+ subtitleFilename: subtitleFilePath,
545
+ subtitleAlignmentAss: subtitleAlignmentAss,
546
+ })), { fps: FPS }), (pass.audioSegment && { audioSegment: pass.audioSegment })), (hookAudio && {
547
+ overlayAudio: { filename: hookAudio.path, durationSeconds: hookAudio.frameCount / FPS },
548
+ })), (ffmpeg_cfr && { crf: ffmpeg_cfr })), (ffmpeg_preset && { preset: ffmpeg_preset })), { enhanceFilters: config_1.getEnhanceFilters(config) }));
549
+ exitPromise = video_1.waitForProcessExit(ffmpegVideoWriter, videoTimeouts.waitForProcessExit);
550
+ stoppedEarly = false;
551
+ i = 0;
552
+ _d.label = 2;
553
+ case 2:
554
+ if (!(i < pass.frameCount)) return [3 /*break*/, 10];
555
+ spectrum = pass.spectrums[i];
556
+ return [4 /*yield*/, resolveBackgroundFrameBuffer({
557
+ frameIndex: i,
558
+ useVideoBackground: useVideoBackground,
559
+ videoFrameReader: videoFrameReader,
560
+ videoFrameSize: videoFrameSize,
561
+ encodeVideoFrame: encodeVideoFrame,
562
+ staticBackgroundBuffer: staticBackgroundBuffer,
563
+ readVideoFrameTimeout: videoTimeouts.readVideoFrame,
564
+ })];
565
+ case 3:
566
+ _b = _d.sent(), backgroundImageBuffer = _b.frameBuffer, frameReadFailed = _b.frameReadFailed;
567
+ commonVisualizerFrameProps = {
568
+ backgroundImageBuffer: backgroundImageBuffer,
569
+ spectrum: spectrum,
570
+ };
571
+ frameImage = createVisualizerFrame(commonVisualizerFrameProps);
572
+ zoomScale = image_1.getCutZoomScale(i, zoomFrames, zoomScales);
573
+ if (zoomScale > 1) {
574
+ image_1.applyCutZoom(frameImage, backgroundWidth, backgroundHeight, zoomScale);
575
+ }
576
+ shakeOffset = image_1.getCutShakeOffset(i, shakeFrames, shakeAmplitudes, shakeDelayFrames);
577
+ if (shakeOffset.x !== 0 || shakeOffset.y !== 0) {
578
+ image_1.applyCameraShake(frameImage, backgroundWidth, backgroundHeight, shakeOffset.x, shakeOffset.y);
579
+ }
580
+ isFrameProcessed = ffmpegVideoWriter.stdin.write(frameImage.data);
581
+ if (!!isFrameProcessed) return [3 /*break*/, 5];
582
+ return [4 /*yield*/, video_1.waitDrain(ffmpegVideoWriter.stdin, ffmpegVideoWriter, videoTimeouts.waitDrain)];
583
+ case 4:
584
+ isDrained = _d.sent();
585
+ if (!isDrained) {
586
+ stoppedEarly = true;
587
+ exitReason = 'ffmpeg stdin drain failed';
588
+ return [3 /*break*/, 10];
589
+ }
590
+ _d.label = 5;
591
+ case 5:
592
+ if (shouldStop && shouldStop()) {
593
+ stoppedEarly = true;
594
+ exitReason = 'render stopped by shouldStop callback';
595
+ return [3 /*break*/, 10];
596
+ }
597
+ if (!frame_processing_delay) return [3 /*break*/, 7];
598
+ return [4 /*yield*/, sleep(frame_processing_delay)];
599
+ case 6:
600
+ _d.sent();
601
+ _d.label = 7;
602
+ case 7: return [4 /*yield*/, waitForEventLoop_1.waitForEventLoop()];
603
+ case 8:
604
+ _d.sent();
605
+ reportRenderProgress();
606
+ _d.label = 9;
607
+ case 9:
608
+ i++;
609
+ return [3 /*break*/, 2];
610
+ case 10:
611
+ if (videoFrameReader) {
612
+ videoFrameReader.kill();
613
+ }
614
+ if (concatFilePath) {
615
+ video_1.cleanupConcatFile(concatFilePath);
616
+ }
617
+ ffmpegVideoWriter.stdin.end();
618
+ return [4 /*yield*/, exitPromise];
619
+ case 11:
620
+ _c = _d.sent(), exitCode = _c.exitCode, ffmpegExitReason = _c.reason;
621
+ lastExitCode = exitCode;
622
+ if (!exitReason && ffmpegExitReason) {
623
+ exitReason = ffmpegExitReason;
624
+ }
625
+ if (lastExitCode === 0 && !stoppedEarly) {
626
+ reportRenderProgress();
627
+ }
628
+ if (lastExitCode === 0 && !stoppedEarly) {
629
+ outputVideoFiles.push(pass.outPath);
630
+ }
631
+ if (stoppedEarly || lastExitCode !== 0) {
632
+ if (exitReason && lastExitCode === 0) {
633
+ lastExitCode = 1;
634
+ }
635
+ return [2 /*return*/, "break-passLoop"];
636
+ }
637
+ return [2 /*return*/];
638
+ }
639
+ });
640
+ };
641
+ passIndex = 0;
642
+ _e.label = 10;
643
+ case 10:
644
+ if (!(passIndex < passes.length)) return [3 /*break*/, 13];
645
+ return [5 /*yield**/, _loop_1(passIndex)];
646
+ case 11:
647
+ state_1 = _e.sent();
648
+ switch (state_1) {
649
+ case "break-passLoop": return [3 /*break*/, 13];
650
+ }
651
+ _e.label = 12;
652
+ case 12:
653
+ passIndex++;
654
+ return [3 /*break*/, 10];
655
+ case 13:
656
+ if (lastExitCode === 0 && outputVideoFiles.length === passes.length) {
657
+ reportProgress(100);
658
+ }
659
+ if (exitReason) {
660
+ console.error(exitReason);
661
+ }
662
+ resolve({ exitCode: lastExitCode, reason: exitReason, outputVideoFiles: outputVideoFiles });
663
+ return [3 /*break*/, 15];
664
+ case 14:
665
+ if (subtitleFilePath && subtitleFileIsTemporary) {
666
+ video_1.cleanupTempFile(subtitleFilePath);
667
+ }
668
+ return [7 /*endfinally*/];
669
+ case 15: return [2 /*return*/];
670
+ }
671
+ });
672
+ }); });
673
+ };
668
674
  //# sourceMappingURL=index.js.map