nodejs-audio-visualizer 4.3.7 → 4.3.9

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