nodejs-audio-visualizer 2.1.3 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -5
- package/dist/audio.d.ts +1 -2
- package/dist/audio.js +44 -31
- package/dist/audio.js.map +1 -1
- package/dist/config.d.ts +5 -5
- package/dist/config.js +11 -11
- package/dist/config.js.map +1 -1
- package/dist/dsp.d.ts +15 -1
- package/dist/dsp.js +58 -7
- package/dist/dsp.js.map +1 -1
- package/dist/image.d.ts +3 -1
- package/dist/image.js +64 -18
- package/dist/image.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +38 -47
- package/dist/index.js.map +1 -1
- package/dist/video.js +8 -2
- package/dist/video.js.map +1 -1
- package/package.json +6 -4
- package/src/audio.ts +40 -35
- package/src/config.ts +15 -12
- package/src/dsp.ts +76 -7
- package/src/image.ts +76 -20
- package/src/index.ts +18 -8
- package/src/tests/audio.test.ts +5 -14
- package/src/tests/config.test.ts +6 -8
- package/src/tests/dsp.test.ts +3 -4
- package/src/tests/image.test.ts +20 -10
- package/src/tests/index.test.ts +1 -1
- package/src/typings.d.ts +7 -0
- package/src/video.ts +4 -1
- package/dist/spectrum.d.ts +0 -1
- package/dist/spectrum.js +0 -72
- package/dist/spectrum.js.map +0 -1
- package/src/spectrum.ts +0 -91
- package/src/tests/snapshots/frameSnapshotSpectrumDown.json +0 -1
- package/src/tests/snapshots/frameSnapshotSpectrumUp.json +0 -1
- package/src/tests/snapshots/frameSnapshotSpectrumUpOpacity50.json +0 -1
package/dist/index.js
CHANGED
|
@@ -58,12 +58,13 @@ exports.PCM_FORMAT = {
|
|
|
58
58
|
parseFunction: audio_1.bufferToUInt8
|
|
59
59
|
};
|
|
60
60
|
var FFMPEG_FORMAT = "" + exports.PCM_FORMAT.sign + exports.PCM_FORMAT.bit;
|
|
61
|
+
var PROCESSING_BUFFER_SIZE = Math.pow(2, 12);
|
|
61
62
|
var sleep = function (timeout) {
|
|
62
63
|
return new Promise(function (resolve) { return setTimeout(resolve, timeout); });
|
|
63
64
|
};
|
|
64
65
|
exports.renderAudioVisualizer = function (config, onProgress, shouldStop) {
|
|
65
66
|
return new Promise(function (resolve) { return __awaiter(void 0, void 0, void 0, function () {
|
|
66
|
-
var audioFilePath, backgroundImagePath, outVideoPath, backgroundImageBmpBuffer, backgroundImage, audioReader, audioBuffer, sampleRate,
|
|
67
|
+
var audioFilePath, backgroundImagePath, outVideoPath, backgroundImageBmpBuffer, backgroundImage, audioReader, audioBuffer, sampleRate, spectrumBusMargin, FPS, spectrumWidth, spectrumHeight, spectrumX, spectrumY, spectrumRotation, spectrumColor, spectrumEffect, spectrumOpacity, ffmpeg_cfr, ffmpeg_preset, frame_processing_delay, audioDuration, framesCount, audioDataStep, processingBuffer, ffmpegVideoWriter, bpmEncoder, backgroundImageBuffer, skipFramesCount, processSpectrum, createVisualizerFrame, i, currentFrameData, audioDataParser, spectrum, frameImage, isFrameProcessed;
|
|
67
68
|
return __generator(this, function (_a) {
|
|
68
69
|
switch (_a.label) {
|
|
69
70
|
case 0:
|
|
@@ -82,7 +83,6 @@ exports.renderAudioVisualizer = function (config, onProgress, shouldStop) {
|
|
|
82
83
|
if (!sampleRate) {
|
|
83
84
|
throw new Error('ffmpeg didn\'t show audio sample rate');
|
|
84
85
|
}
|
|
85
|
-
spectrumBusesCount = config_1.getSpectrumBusesCount();
|
|
86
86
|
spectrumBusMargin = config_1.getSpectrumBusMargin();
|
|
87
87
|
FPS = config_1.getFPS(config);
|
|
88
88
|
spectrumWidth = config_1.getSpectrumWidthAbsolute(config, backgroundImage.width);
|
|
@@ -92,6 +92,7 @@ exports.renderAudioVisualizer = function (config, onProgress, shouldStop) {
|
|
|
92
92
|
spectrumRotation = config_1.getSpectrumRotation(config);
|
|
93
93
|
spectrumColor = config_1.getSpectrumColor(config) ||
|
|
94
94
|
image_1.invertColor(image_1.getImageColor(backgroundImage));
|
|
95
|
+
spectrumEffect = config_1.getSpectrumEffect(config);
|
|
95
96
|
spectrumOpacity = config_1.getSpectrumOpacityParsed(config);
|
|
96
97
|
ffmpeg_cfr = config_1.getFfmpeg_cfr(config);
|
|
97
98
|
ffmpeg_preset = config_1.getFfmpeg_preset(config);
|
|
@@ -99,63 +100,53 @@ exports.renderAudioVisualizer = function (config, onProgress, shouldStop) {
|
|
|
99
100
|
audioDuration = audioBuffer.length / sampleRate;
|
|
100
101
|
framesCount = Math.trunc(audioDuration * FPS);
|
|
101
102
|
audioDataStep = Math.trunc(audioBuffer.length / framesCount);
|
|
103
|
+
processingBuffer = new Float32Array(PROCESSING_BUFFER_SIZE).fill(0);
|
|
102
104
|
ffmpegVideoWriter = video_1.spawnFfmpegVideoWriter(__assign(__assign(__assign({ audioFilename: audioFilePath, videoFileName: outVideoPath, fps: FPS }, (!!onProgress && { onStderr: video_1.getProgress(video_1.calculateProgress(framesCount + 1, onProgress)) })), (ffmpeg_cfr && { crf: ffmpeg_cfr })), (ffmpeg_preset && { preset: ffmpeg_preset })));
|
|
103
105
|
ffmpegVideoWriter.on('exit', function (code) { return resolve(code); });
|
|
104
106
|
bpmEncoder = bpmEncoder_1.createBpmEncoder({ width: backgroundImage.width, height: backgroundImage.height });
|
|
105
107
|
backgroundImageBuffer = bpmEncoder(backgroundImage.data);
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
return __generator(this, function (_a) {
|
|
110
|
-
switch (_a.label) {
|
|
111
|
-
case 0:
|
|
112
|
-
audioDataParser = function () {
|
|
113
|
-
return exports.PCM_FORMAT.parseFunction(audioBuffer, i * audioDataStep, i * audioDataStep + audioDataStep);
|
|
114
|
-
};
|
|
115
|
-
spectrum = processSpectrum(i, audioDataParser);
|
|
116
|
-
frameImage = image_1.createVisualizerFrame({
|
|
117
|
-
backgroundImageBuffer: backgroundImageBuffer,
|
|
118
|
-
spectrum: spectrum,
|
|
119
|
-
size: { width: spectrumWidth, height: spectrumHeight },
|
|
120
|
-
position: { x: spectrumX, y: spectrumY },
|
|
121
|
-
rotation: spectrumRotation,
|
|
122
|
-
margin: spectrumBusMargin,
|
|
123
|
-
color: spectrumColor,
|
|
124
|
-
opacity: spectrumOpacity,
|
|
125
|
-
});
|
|
126
|
-
isFrameProcessed = ffmpegVideoWriter.stdin.write(frameImage.data);
|
|
127
|
-
if (!!isFrameProcessed) return [3 /*break*/, 2];
|
|
128
|
-
return [4 /*yield*/, video_1.waitDrain(ffmpegVideoWriter.stdin)];
|
|
129
|
-
case 1:
|
|
130
|
-
_a.sent();
|
|
131
|
-
_a.label = 2;
|
|
132
|
-
case 2:
|
|
133
|
-
if (shouldStop && shouldStop()) {
|
|
134
|
-
return [2 /*return*/, "break"];
|
|
135
|
-
}
|
|
136
|
-
if (!frame_processing_delay) return [3 /*break*/, 4];
|
|
137
|
-
return [4 /*yield*/, sleep(frame_processing_delay)];
|
|
138
|
-
case 3:
|
|
139
|
-
_a.sent();
|
|
140
|
-
_a.label = 4;
|
|
141
|
-
case 4: return [2 /*return*/];
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
};
|
|
108
|
+
skipFramesCount = FPS < 45 ? 1 : 2;
|
|
109
|
+
processSpectrum = audio_1.createSpectrumsProcessor(sampleRate, skipFramesCount);
|
|
110
|
+
createVisualizerFrame = image_1.createVisualizerFrameGenerator();
|
|
145
111
|
i = 0;
|
|
146
112
|
_a.label = 3;
|
|
147
113
|
case 3:
|
|
148
|
-
if (!(i < framesCount)) return [3 /*break*/,
|
|
149
|
-
|
|
114
|
+
if (!(i < framesCount)) return [3 /*break*/, 8];
|
|
115
|
+
currentFrameData = exports.PCM_FORMAT.parseFunction(audioBuffer, i * audioDataStep, i * audioDataStep + audioDataStep);
|
|
116
|
+
processingBuffer.copyWithin(0, currentFrameData.length);
|
|
117
|
+
processingBuffer.set(currentFrameData, PROCESSING_BUFFER_SIZE - currentFrameData.length);
|
|
118
|
+
audioDataParser = function () { return Array.from(processingBuffer); };
|
|
119
|
+
spectrum = processSpectrum(audioDataParser);
|
|
120
|
+
frameImage = createVisualizerFrame({
|
|
121
|
+
backgroundImageBuffer: backgroundImageBuffer,
|
|
122
|
+
spectrum: spectrum,
|
|
123
|
+
size: { width: spectrumWidth, height: spectrumHeight },
|
|
124
|
+
position: { x: spectrumX, y: spectrumY },
|
|
125
|
+
rotation: spectrumRotation,
|
|
126
|
+
margin: spectrumBusMargin,
|
|
127
|
+
color: spectrumColor,
|
|
128
|
+
opacity: spectrumOpacity,
|
|
129
|
+
spectrumEffect: spectrumEffect,
|
|
130
|
+
});
|
|
131
|
+
isFrameProcessed = ffmpegVideoWriter.stdin.write(frameImage.data);
|
|
132
|
+
if (!!isFrameProcessed) return [3 /*break*/, 5];
|
|
133
|
+
return [4 /*yield*/, video_1.waitDrain(ffmpegVideoWriter.stdin)];
|
|
150
134
|
case 4:
|
|
151
|
-
|
|
152
|
-
if (state_1 === "break")
|
|
153
|
-
return [3 /*break*/, 6];
|
|
135
|
+
_a.sent();
|
|
154
136
|
_a.label = 5;
|
|
155
137
|
case 5:
|
|
138
|
+
if (shouldStop && shouldStop()) {
|
|
139
|
+
return [3 /*break*/, 8];
|
|
140
|
+
}
|
|
141
|
+
if (!frame_processing_delay) return [3 /*break*/, 7];
|
|
142
|
+
return [4 /*yield*/, sleep(frame_processing_delay)];
|
|
143
|
+
case 6:
|
|
144
|
+
_a.sent();
|
|
145
|
+
_a.label = 7;
|
|
146
|
+
case 7:
|
|
156
147
|
i++;
|
|
157
148
|
return [3 /*break*/, 3];
|
|
158
|
-
case
|
|
149
|
+
case 8:
|
|
159
150
|
ffmpegVideoWriter.stdin.end();
|
|
160
151
|
return [2 /*return*/];
|
|
161
152
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/video.js
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
var child_process_1 = require("child_process");
|
|
4
|
-
var ffmpeg_static_1 = require("ffmpeg-static");
|
|
7
|
+
var ffmpeg_static_1 = __importDefault(require("ffmpeg-static"));
|
|
5
8
|
exports.spawnFfmpegVideoWriter = function (config) {
|
|
9
|
+
if (!ffmpeg_static_1.default) {
|
|
10
|
+
throw new Error('ffmpeg path not found');
|
|
11
|
+
}
|
|
6
12
|
var crf = config.crf || '23';
|
|
7
13
|
var preset = config.preset || 'medium';
|
|
8
14
|
var args = [
|
|
@@ -14,7 +20,7 @@ exports.spawnFfmpegVideoWriter = function (config) {
|
|
|
14
20
|
'-r', "" + config.fps,
|
|
15
21
|
'-i', '-'
|
|
16
22
|
];
|
|
17
|
-
var ffmpeg = child_process_1.spawn(ffmpeg_static_1.
|
|
23
|
+
var ffmpeg = child_process_1.spawn(ffmpeg_static_1.default, args);
|
|
18
24
|
ffmpeg.stdin.pipe(process.stdout);
|
|
19
25
|
if (config.onStderr) {
|
|
20
26
|
ffmpeg.stderr.on('data', config.onStderr);
|
package/dist/video.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video.js","sourceRoot":"","sources":["../src/video.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"video.js","sourceRoot":"","sources":["../src/video.ts"],"names":[],"mappings":";;;;;AACA,+CAAsC;AACtC,gEAAuC;AAW1B,QAAA,sBAAsB,GAAG,UAAC,MAA+B;IACpE,IAAI,CAAC,uBAAU,EAAE;QACf,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;KAC1C;IACD,IAAM,GAAG,GAAG,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC;IAC/B,IAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,QAAQ,CAAC;IACzC,IAAM,IAAI,GAAG;QACX,IAAI;QACJ,IAAI,EAAE,MAAM,CAAC,aAAa;QAC1B,MAAM,EAAE,GAAG;QACX,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,SAAS;QACtD,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,KAAG,MAAM,CAAC,GAAK,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,aAAa;QACxG,IAAI,EAAE,KAAG,MAAM,CAAC,GAAK;QACrB,IAAI,EAAE,GAAG;KACV,CAAC;IACF,IAAM,MAAM,GAAG,qBAAK,CAAC,uBAAU,EAAE,IAAI,CAAC,CAAC;IACvC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,MAAM,CAAC,QAAQ,EAAE;QACnB,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;KAC3C;IACD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEW,QAAA,WAAW,GAAG,UAAC,UAAyC;IACnE,OAAA,UAAC,YAAoB;QACnB,IAAM,WAAW,GAAG,YAAY,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACrE,IAAI,CAAC,WAAW,EAAE;YAChB,OAAO;SACR;QACD,IAAM,YAAY,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;QACrC,IAAI,KAAK,CAAC,YAAY,CAAC,EAAE;YACvB,OAAO;SACR;QACD,UAAU,CAAC,YAAY,CAAC,CAAC;IAC3B,CAAC;AAVD,CAUC,CAAC;AAES,QAAA,iBAAiB,GAAG,UAAC,WAAmB,EAAE,QAAmC;IACxF,OAAA,UAAC,YAAoB;QACnB,OAAA,QAAQ,CACN,CAAC,CAAC,YAAY,GAAG,WAAW,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAC/C;IAFD,CAEC;AAHH,CAGG,CAAC;AAEO,QAAA,SAAS,GAAG,UAAC,MAAgB;IACxC,OAAA,IAAI,OAAO,CAAO,UAAA,OAAO,IAAI,OAAA,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,EAA7B,CAA6B,CAAC;AAA3D,CAA2D,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodejs-audio-visualizer",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"src/"
|
|
10
10
|
],
|
|
11
11
|
"scripts": {
|
|
12
|
-
"test": "mocha -
|
|
12
|
+
"test": "ts-mocha -p tsconfig.json src/**/*.test.ts",
|
|
13
13
|
"dev": "ts-node-dev --respawn --transpileOnly ./example/example.ts",
|
|
14
14
|
"prebuild": "rimraf dist",
|
|
15
15
|
"build": "tsc -m CommonJS",
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"mocha": "^6.2.3",
|
|
38
38
|
"rimraf": "^3.0.2",
|
|
39
39
|
"sinon": "^7.5.0",
|
|
40
|
+
"ts-mocha": "^11.1.0",
|
|
40
41
|
"ts-node": "^8.5.0",
|
|
41
42
|
"ts-node-dev": "^1.0.0-pre.43",
|
|
42
43
|
"typescript": "^3.7.2"
|
|
@@ -50,10 +51,11 @@
|
|
|
50
51
|
"@jimp/bmp": "^0.9.3",
|
|
51
52
|
"@jimp/png": "^0.9.3",
|
|
52
53
|
"@types/bmp-js": "^0.1.0",
|
|
53
|
-
"@types/ffmpeg-static": "^
|
|
54
|
+
"@types/ffmpeg-static": "^3.0.3",
|
|
54
55
|
"@types/sinon": "^7.5.0",
|
|
55
56
|
"bmp-js": "^0.1.0",
|
|
56
|
-
"ffmpeg-static": "^2.
|
|
57
|
+
"ffmpeg-static": "^5.2.0",
|
|
58
|
+
"fft-js": "^0.0.12",
|
|
57
59
|
"jimp": "^0.9.3"
|
|
58
60
|
}
|
|
59
61
|
}
|
package/src/audio.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { spawn } from 'child_process';
|
|
2
|
-
import
|
|
3
|
-
import { getSpectrum } from './dsp';
|
|
2
|
+
import ffmpegPath from 'ffmpeg-static';
|
|
3
|
+
import { FREQUENCY_BANDS, getSpectrum } from './dsp';
|
|
4
4
|
|
|
5
5
|
export const skipEvery = <T>(skipIndex: number) => (element: T, index: number) =>
|
|
6
6
|
index % skipIndex === 0;
|
|
@@ -34,44 +34,46 @@ export const correctPeaks = (spectrums: number[], peaks: number[]) => {
|
|
|
34
34
|
return resultSpectrum;
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
const currValue = spectrums[i];
|
|
44
|
-
const currPrevValue = prevSpectrums[i] || 0;
|
|
45
|
-
const avgValue = (currValue + currPrevValue) / 2;
|
|
46
|
-
resultSpectrum.push(avgValue);
|
|
47
|
-
}
|
|
48
|
-
return resultSpectrum;
|
|
37
|
+
const oldMax = 1;
|
|
38
|
+
const newMin = 0.6;
|
|
39
|
+
const getInterpolationFactor = (skippedFramesCount: number, skipFramesCount: number) => {
|
|
40
|
+
const interpolationFactor = 1 - (skippedFramesCount / skipFramesCount);
|
|
41
|
+
const interpolationFactorInRange = (interpolationFactor * (oldMax - newMin)) + newMin;
|
|
42
|
+
return interpolationFactorInRange;
|
|
49
43
|
};
|
|
50
44
|
|
|
51
|
-
export const createSpectrumsProcessor = (
|
|
45
|
+
export const createSpectrumsProcessor = (sampleRate: number, skipFramesCount: number) => {
|
|
52
46
|
let prevAudioDataNormalized: number[] = [];
|
|
53
47
|
let prevPeaks: number[] = [];
|
|
54
|
-
let
|
|
55
|
-
|
|
48
|
+
let skippedFramesCount = 0;
|
|
49
|
+
let targetSpectrum: number[] = [];
|
|
50
|
+
let previousSpectrum: number[] = [];
|
|
56
51
|
|
|
57
|
-
return (
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
prevAudioDataNormalized
|
|
62
|
-
normalizeAudioData(parseAudioData());
|
|
63
|
-
prevAudioDataNormalized = audioDataNomrmalized;
|
|
52
|
+
return (parseAudioData: () => number[]) => {
|
|
53
|
+
if (skippedFramesCount === 0) {
|
|
54
|
+
skippedFramesCount = skipFramesCount;
|
|
55
|
+
const audioDataNomrmalized = normalizeAudioData(parseAudioData());
|
|
56
|
+
prevAudioDataNormalized = audioDataNomrmalized;
|
|
64
57
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
58
|
+
const spectrum = getSpectrum(audioDataNomrmalized, sampleRate);
|
|
59
|
+
const peaks = getPeaks(spectrum, prevPeaks);
|
|
60
|
+
const correctedSpectrum =
|
|
61
|
+
prevPeaks.length === 0 ?
|
|
62
|
+
Array(FREQUENCY_BANDS.length).fill(0) :
|
|
63
|
+
correctPeaks(spectrum, peaks);
|
|
64
|
+
|
|
65
|
+
prevPeaks = peaks;
|
|
66
|
+
previousSpectrum = [...targetSpectrum];
|
|
67
|
+
targetSpectrum = correctedSpectrum;
|
|
68
|
+
} else {
|
|
69
|
+
skippedFramesCount--;
|
|
70
|
+
}
|
|
71
|
+
const interpolationFactor = getInterpolationFactor(skippedFramesCount, skipFramesCount);
|
|
72
|
+
const interpolatedSpectrum = targetSpectrum.map((target, i) => {
|
|
73
|
+
const prev = previousSpectrum[i] || target;
|
|
74
|
+
return prev + (target - prev) * interpolationFactor;
|
|
75
|
+
});
|
|
76
|
+
return interpolatedSpectrum;
|
|
75
77
|
};
|
|
76
78
|
};
|
|
77
79
|
|
|
@@ -87,7 +89,10 @@ export const normalizeAudioData = (PCMData: number[]) =>
|
|
|
87
89
|
PCMData.map(num => (num - 128) / 128);
|
|
88
90
|
|
|
89
91
|
export const spawnFfmpegAudioReader = (filename: string, format: string) => {
|
|
90
|
-
|
|
92
|
+
if (!ffmpegPath) {
|
|
93
|
+
throw new Error('ffmpeg path not found');
|
|
94
|
+
}
|
|
95
|
+
const ffmpegProcess = spawn(ffmpegPath, ['-i', filename, '-f', format, '-ac', '1', '-']);
|
|
91
96
|
return ffmpegProcess;
|
|
92
97
|
};
|
|
93
98
|
|
package/src/config.ts
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import { Config, SpectrumSizeValue, PositionAliasName, RotationAliasName } from './index';
|
|
3
|
+
import { FREQUENCY_BANDS } from './dsp';
|
|
3
4
|
|
|
4
5
|
export const defaults = {
|
|
5
|
-
fps:
|
|
6
|
-
spectrumWidth: '
|
|
7
|
-
spectrumHeight:
|
|
6
|
+
fps: 30,
|
|
7
|
+
spectrumWidth: '33%',
|
|
8
|
+
spectrumHeight: 160,
|
|
8
9
|
spectrumX: 'center',
|
|
9
|
-
spectrumY: '
|
|
10
|
-
spectrumRotation: '
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
spectrumOpacity: '100%',
|
|
10
|
+
spectrumY: 'middle',
|
|
11
|
+
spectrumRotation: 'mirror',
|
|
12
|
+
spectrumBusMargin: 12,
|
|
13
|
+
spectrumOpacity: '80%',
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
type RelativePositionValue = 0 | 0.5 | 1;
|
|
17
17
|
|
|
18
|
+
export type SpectrumEffect = 'volume' | 'smooth';
|
|
19
|
+
|
|
18
20
|
interface PositionAlias {
|
|
19
21
|
name: PositionAliasName;
|
|
20
22
|
value: RelativePositionValue;
|
|
@@ -29,7 +31,7 @@ const postitionAliases: PositionAlias[] = [
|
|
|
29
31
|
{ name: 'bottom', value: 1 },
|
|
30
32
|
];
|
|
31
33
|
|
|
32
|
-
export const rotationAliasValues = ['up', 'down'] as const;
|
|
34
|
+
export const rotationAliasValues = ['up', 'down', 'mirror'] as const;
|
|
33
35
|
|
|
34
36
|
export const checkIsPercentValue = (value: string) =>
|
|
35
37
|
RegExp(/\d+%/).test(value)
|
|
@@ -46,14 +48,15 @@ export const getOutVideoPath = (config: Config) =>
|
|
|
46
48
|
export const getFPS = (config: Config) =>
|
|
47
49
|
config.outVideo.fps || defaults.fps;
|
|
48
50
|
|
|
49
|
-
export const getSpectrumBusesCount = () => defaults.spectrumBusesCount;
|
|
50
|
-
|
|
51
51
|
export const getSpectrumBusMargin = () => defaults.spectrumBusMargin;
|
|
52
52
|
|
|
53
53
|
const getSpectrumWidth = (config: Config) =>
|
|
54
54
|
(config.outVideo.spectrum && config.outVideo.spectrum.width) ||
|
|
55
55
|
defaults.spectrumWidth;
|
|
56
56
|
|
|
57
|
+
export const getSpectrumEffect = (config: Config) =>
|
|
58
|
+
config.outVideo.spectrum?.effect;
|
|
59
|
+
|
|
57
60
|
const getSpectrumSizeAbsolute = (
|
|
58
61
|
spectrumSize: SpectrumSizeValue,
|
|
59
62
|
backgroundImageSize: number,
|
|
@@ -81,7 +84,7 @@ export const getSpectrumWidthAbsolute = (
|
|
|
81
84
|
const spectrumWidth = getSpectrumWidth(config);
|
|
82
85
|
const spectrumWidthAbsolute = getSpectrumSizeAbsolute(spectrumWidth, backgroundImageWidth, 'width');
|
|
83
86
|
const spectrumWidthWithoutMargin =
|
|
84
|
-
Math.trunc(spectrumWidthAbsolute /
|
|
87
|
+
Math.trunc(spectrumWidthAbsolute / FREQUENCY_BANDS.length) - defaults.spectrumBusMargin / 2;
|
|
85
88
|
if (spectrumWidthWithoutMargin <= 0) {
|
|
86
89
|
throw new Error(`Spectrum width '${spectrumWidth}' is too small.`);
|
|
87
90
|
}
|
package/src/dsp.ts
CHANGED
|
@@ -1,4 +1,62 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { fft, util } from 'fft-js';
|
|
2
|
+
|
|
3
|
+
export interface SpectrumPoint {
|
|
4
|
+
frequency: number;
|
|
5
|
+
magnitude: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface FrequencyBus {
|
|
9
|
+
startFreq: number;
|
|
10
|
+
endFreq: number;
|
|
11
|
+
magnitude: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Common frequency bands (in Hz)
|
|
15
|
+
export const FREQUENCY_BANDS = [
|
|
16
|
+
{ name: 'Sub Bass 1', start: 20, end: 30 },
|
|
17
|
+
{ name: 'Sub Bass 2', start: 30, end: 40 },
|
|
18
|
+
{ name: 'Sub Bass 3', start: 50, end: 60 },
|
|
19
|
+
{ name: 'Sub Bass 4', start: 20, end: 60 },
|
|
20
|
+
{ name: 'Bass 1', start: 60, end: 107.5 },
|
|
21
|
+
{ name: 'Bass 2', start: 107.5, end: 155 },
|
|
22
|
+
{ name: 'Bass 3', start: 155, end: 202.5 },
|
|
23
|
+
{ name: 'Bass 4', start: 202.5, end: 250 },
|
|
24
|
+
{ name: 'Low Mids 1', start: 250, end: 312.5 },
|
|
25
|
+
{ name: 'Low Mids 2', start: 312.5, end: 375 },
|
|
26
|
+
{ name: 'Low Mids 3', start: 375, end: 437.5 },
|
|
27
|
+
{ name: 'Low Mids 4', start: 437.5, end: 500 },
|
|
28
|
+
{ name: 'Mids 1', start: 500, end: 875 },
|
|
29
|
+
{ name: 'Mids 2', start: 875, end: 1250 },
|
|
30
|
+
{ name: 'Mids 3', start: 1250, end: 1625 },
|
|
31
|
+
{ name: 'Mids 4', start: 1625, end: 2000 },
|
|
32
|
+
{ name: 'High Mids 1', start: 2000, end: 2500 },
|
|
33
|
+
{ name: 'High Mids 2', start: 2500, end: 3000 },
|
|
34
|
+
{ name: 'High Mids 3', start: 3000, end: 3500 },
|
|
35
|
+
{ name: 'High Mids 4', start: 3500, end: 4000 },
|
|
36
|
+
{ name: 'Presence 1', start: 4000, end: 5000 },
|
|
37
|
+
{ name: 'Presence 2', start: 5000, end: 6000 },
|
|
38
|
+
{ name: 'Brilliance 1', start: 6000, end: 13000 },
|
|
39
|
+
{ name: 'Brilliance 2', start: 13000, end: 20000 },
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const combineToBuses = (spectrum: SpectrumPoint[]): number[] => {
|
|
43
|
+
return FREQUENCY_BANDS.map(band => {
|
|
44
|
+
const pointsInBand = spectrum.filter(point =>
|
|
45
|
+
point.frequency >= band.start && point.frequency < band.end
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
// If no points found in this band, return zero magnitude
|
|
49
|
+
if (pointsInBand.length === 0) {
|
|
50
|
+
return 0;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Average the magnitudes in this frequency band
|
|
54
|
+
const avgMagnitude = pointsInBand.reduce((sum, point) =>
|
|
55
|
+
sum + point.magnitude, 0) / pointsInBand.length;
|
|
56
|
+
|
|
57
|
+
return avgMagnitude;
|
|
58
|
+
});
|
|
59
|
+
};
|
|
2
60
|
|
|
3
61
|
export const brutForceFFTSignalLength = (PCMDataLength: number) => {
|
|
4
62
|
let exponent = 1;
|
|
@@ -11,16 +69,27 @@ export const brutForceFFTSignalLength = (PCMDataLength: number) => {
|
|
|
11
69
|
}
|
|
12
70
|
};
|
|
13
71
|
|
|
14
|
-
export const getSpectrum = (PCMData: number[]) => {
|
|
72
|
+
export const getSpectrum = (PCMData: number[], sampleRate: number): number[] => {
|
|
15
73
|
const signalLength = brutForceFFTSignalLength(PCMData.length);
|
|
16
74
|
const signalStartIndex = Math.trunc(PCMData.length / 2 - signalLength / 2);
|
|
17
75
|
const signal = PCMData.slice(signalStartIndex, signalStartIndex + signalLength);
|
|
18
76
|
|
|
19
|
-
|
|
20
|
-
const
|
|
77
|
+
// Use fft-js to compute the FFT
|
|
78
|
+
const phasors = fft(signal);
|
|
79
|
+
const frequencies = util.fftFreq(phasors, sampleRate);
|
|
80
|
+
const magnitudes = util.fftMag(phasors);
|
|
81
|
+
|
|
82
|
+
// Create combined spectrum array with both frequency and magnitude
|
|
83
|
+
let spectrum: SpectrumPoint[] = frequencies.map((freq: number, i: number) => ({
|
|
84
|
+
frequency: freq,
|
|
85
|
+
magnitude: magnitudes[i]
|
|
86
|
+
}));
|
|
87
|
+
|
|
88
|
+
// Apply frequency-dependent scaling
|
|
21
89
|
for (let i = 0; i < spectrum.length; i++) {
|
|
22
|
-
|
|
23
|
-
spectrum[i] *= -1 * Math.log((signalLength / 2 - i) * (0.5 / signalLength / 2)) * signalLength;
|
|
90
|
+
spectrum[i].magnitude *= -1 * Math.log((signalLength / 2 - i) * (0.5 / signalLength / 2)) * signalLength;
|
|
24
91
|
}
|
|
25
|
-
|
|
92
|
+
|
|
93
|
+
// Combine frequencies into buses
|
|
94
|
+
return combineToBuses(spectrum);
|
|
26
95
|
}
|
package/src/image.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { RotationAliasName } from './index';
|
|
|
2
2
|
import { decode, BmpDecoder } from 'bmp-js';
|
|
3
3
|
import { EncodedBmp } from './bpmEncoder';
|
|
4
4
|
import Jimp from 'jimp';
|
|
5
|
+
import { SpectrumEffect } from './config';
|
|
5
6
|
|
|
6
7
|
export interface Color {
|
|
7
8
|
red: number;
|
|
@@ -111,6 +112,27 @@ interface DrawSpectrumProps {
|
|
|
111
112
|
opacity: number;
|
|
112
113
|
}
|
|
113
114
|
|
|
115
|
+
const getRectY = (rotation: RotationAliasName, top: number, height: number, rectHeight: number) => {
|
|
116
|
+
switch (rotation) {
|
|
117
|
+
case 'up':
|
|
118
|
+
return top + height - rectHeight;
|
|
119
|
+
case 'mirror':
|
|
120
|
+
return top + Math.trunc((height - rectHeight) / 2);
|
|
121
|
+
default:
|
|
122
|
+
return top;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const minRectHeight = 2;
|
|
127
|
+
|
|
128
|
+
const getRectHeight = (height: number, spectrumValue: number) => {
|
|
129
|
+
const rectHeight = Math.trunc(height * spectrumValue);
|
|
130
|
+
if (rectHeight < minRectHeight) {
|
|
131
|
+
return minRectHeight;
|
|
132
|
+
}
|
|
133
|
+
return rectHeight;
|
|
134
|
+
};
|
|
135
|
+
|
|
114
136
|
const drawSpectrum = ({
|
|
115
137
|
imageDstBuffer,
|
|
116
138
|
spectrum,
|
|
@@ -133,8 +155,8 @@ const drawSpectrum = ({
|
|
|
133
155
|
}
|
|
134
156
|
|
|
135
157
|
const rectX = left + busWidth * spectrumX;
|
|
136
|
-
const rectHeight =
|
|
137
|
-
const rectY = (rotation
|
|
158
|
+
const rectHeight = getRectHeight(height, spectrumValue);
|
|
159
|
+
const rectY = getRectY(rotation, top, height, rectHeight);
|
|
138
160
|
drawRect({
|
|
139
161
|
imageDstBuffer,
|
|
140
162
|
position: { x: rectX + margin, y: rectY },
|
|
@@ -154,33 +176,67 @@ interface CreateVisualizerFrameProps {
|
|
|
154
176
|
margin: number;
|
|
155
177
|
color: Color | string;
|
|
156
178
|
opacity: number;
|
|
179
|
+
spectrumEffect?: SpectrumEffect;
|
|
157
180
|
}
|
|
158
181
|
|
|
159
|
-
export const
|
|
160
|
-
|
|
161
|
-
spectrum,
|
|
162
|
-
size,
|
|
163
|
-
position,
|
|
164
|
-
rotation,
|
|
165
|
-
margin,
|
|
166
|
-
color,
|
|
167
|
-
opacity,
|
|
168
|
-
}: CreateVisualizerFrameProps) => {
|
|
169
|
-
const imageDstBuffer = Object.assign({}, backgroundImageBuffer);
|
|
170
|
-
imageDstBuffer.data = Buffer.from(backgroundImageBuffer.data);
|
|
182
|
+
export const createVisualizerFrameGenerator = () => {
|
|
183
|
+
let prevSpectrum: number[] | null = null;
|
|
171
184
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
imageDstBuffer,
|
|
185
|
+
return ({
|
|
186
|
+
backgroundImageBuffer,
|
|
175
187
|
spectrum,
|
|
176
188
|
size,
|
|
177
189
|
position,
|
|
178
190
|
rotation,
|
|
179
191
|
margin,
|
|
180
|
-
color
|
|
192
|
+
color,
|
|
181
193
|
opacity,
|
|
182
|
-
|
|
183
|
-
|
|
194
|
+
spectrumEffect,
|
|
195
|
+
}: CreateVisualizerFrameProps) => {
|
|
196
|
+
const imageDstBuffer = Object.assign({}, backgroundImageBuffer);
|
|
197
|
+
imageDstBuffer.data = Buffer.from(backgroundImageBuffer.data);
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
const rgbSpectrumColor = (typeof color === 'string') ? hexToRgb(color) : color;
|
|
201
|
+
if (spectrumEffect === 'volume') {
|
|
202
|
+
drawSpectrum({
|
|
203
|
+
imageDstBuffer,
|
|
204
|
+
spectrum,
|
|
205
|
+
size,
|
|
206
|
+
position: { x: position.x + 2, y: position.y + 2 },
|
|
207
|
+
rotation,
|
|
208
|
+
margin,
|
|
209
|
+
color: rgbSpectrumColor,
|
|
210
|
+
opacity: opacity * 0.5,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
if (spectrumEffect === 'smooth' && prevSpectrum) {
|
|
214
|
+
drawSpectrum({
|
|
215
|
+
imageDstBuffer,
|
|
216
|
+
spectrum: prevSpectrum,
|
|
217
|
+
size,
|
|
218
|
+
position,
|
|
219
|
+
rotation,
|
|
220
|
+
margin,
|
|
221
|
+
color: rgbSpectrumColor,
|
|
222
|
+
opacity: opacity * 0.3,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
drawSpectrum({
|
|
226
|
+
imageDstBuffer,
|
|
227
|
+
spectrum,
|
|
228
|
+
size,
|
|
229
|
+
position,
|
|
230
|
+
rotation,
|
|
231
|
+
margin,
|
|
232
|
+
color: rgbSpectrumColor,
|
|
233
|
+
opacity,
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
prevSpectrum = spectrum;
|
|
237
|
+
|
|
238
|
+
return imageDstBuffer;
|
|
239
|
+
};
|
|
184
240
|
};
|
|
185
241
|
|
|
186
242
|
export const convertToBmp = async (filePath: string) =>
|