nodejs-audio-visualizer 5.5.3 → 5.6.4
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/LICENSE +21 -21
- package/README.md +84 -83
- package/dist/audio.d.ts +13 -13
- package/dist/audio.js +128 -128
- package/dist/beats.d.ts +39 -39
- package/dist/beats.js +220 -220
- package/dist/bpmEncoder.d.ts +14 -14
- package/dist/bpmEncoder.js +97 -97
- package/dist/config.d.ts +81 -80
- package/dist/config.js +392 -391
- package/dist/config.js.map +1 -1
- package/dist/dsp.d.ts +16 -16
- package/dist/dsp.js +76 -76
- package/dist/highlight.d.ts +91 -91
- package/dist/highlight.js +606 -606
- package/dist/image.d.ts +107 -97
- package/dist/image.js +614 -545
- package/dist/image.js.map +1 -1
- package/dist/index.d.ts +98 -96
- package/dist/index.js +673 -667
- package/dist/index.js.map +1 -1
- package/dist/subtitleConvert.d.ts +3 -3
- package/dist/subtitleConvert.js +61 -61
- package/dist/video.d.ts +171 -171
- package/dist/video.js +743 -743
- package/dist/waitForEventLoop.d.ts +1 -1
- package/dist/waitForEventLoop.js +5 -5
- package/package.json +62 -62
- package/src/audio.ts +129 -129
- package/src/beats.ts +279 -279
- package/src/bpmEncoder.ts +97 -97
- package/src/config.ts +504 -501
- package/src/dsp.ts +95 -95
- package/src/highlight.ts +617 -617
- package/src/image.ts +791 -707
- package/src/index.ts +896 -885
- package/src/subtitleConvert.ts +63 -63
- package/src/tests/audio.test.ts +102 -102
- package/src/tests/beats.test.ts +134 -134
- package/src/tests/config.test.ts +568 -553
- package/src/tests/dsp.test.ts +24 -24
- package/src/tests/highlight.test.ts +397 -397
- package/src/tests/image.test.ts +371 -319
- package/src/tests/index.test.ts +180 -180
- package/src/tests/subtitleConvert.test.ts +23 -23
- package/src/tests/video.test.ts +652 -652
- package/src/typings.d.ts +28 -28
- package/src/video.ts +947 -947
- package/src/waitForEventLoop.ts +2 -2
package/dist/beats.js
CHANGED
|
@@ -1,221 +1,221 @@
|
|
|
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 __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
var music_tempo_1 = __importDefault(require("music-tempo"));
|
|
18
|
-
exports.MIN_TEMPO_BPM = 70;
|
|
19
|
-
exports.MAX_TEMPO_BPM = 180;
|
|
20
|
-
exports.PREFERRED_TEMPO_BPM_MIN = 90;
|
|
21
|
-
exports.PREFERRED_TEMPO_BPM_MAX = 160;
|
|
22
|
-
exports.DEFAULT_BASS_END_INDEX = 8;
|
|
23
|
-
/** Longest PCM window passed to music-tempo (seconds), taken from the start of the audio. */
|
|
24
|
-
exports.MAX_TEMPO_ANALYSIS_SECONDS = 90;
|
|
25
|
-
var TEMPO_TIME_STEP = 0.01;
|
|
26
|
-
exports.frameBassEnergy = function (spectrum, bassEndIndex) {
|
|
27
|
-
if (bassEndIndex === void 0) { bassEndIndex = exports.DEFAULT_BASS_END_INDEX; }
|
|
28
|
-
var end = Math.min(bassEndIndex, spectrum.length);
|
|
29
|
-
if (end <= 0) {
|
|
30
|
-
return 0;
|
|
31
|
-
}
|
|
32
|
-
var sum = 0;
|
|
33
|
-
for (var i = 0; i < end; i++) {
|
|
34
|
-
var v = spectrum[i];
|
|
35
|
-
sum += v * v;
|
|
36
|
-
}
|
|
37
|
-
return sum / end;
|
|
38
|
-
};
|
|
39
|
-
exports.createBeatDetector = function (fps, options) {
|
|
40
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
41
|
-
var historySize = (_b = (_a = options) === null || _a === void 0 ? void 0 : _a.historySize, (_b !== null && _b !== void 0 ? _b : Math.round(fps * 1.5)));
|
|
42
|
-
var thresholdMultiplier = (_d = (_c = options) === null || _c === void 0 ? void 0 : _c.thresholdMultiplier, (_d !== null && _d !== void 0 ? _d : 1.4));
|
|
43
|
-
var decayRate = (_f = (_e = options) === null || _e === void 0 ? void 0 : _e.decayRate, (_f !== null && _f !== void 0 ? _f : 0.85));
|
|
44
|
-
var cooldownFrames = (_h = (_g = options) === null || _g === void 0 ? void 0 : _g.cooldownFrames, (_h !== null && _h !== void 0 ? _h : Math.round(fps * 0.15)));
|
|
45
|
-
var bassEndIndex = (_k = (_j = options) === null || _j === void 0 ? void 0 : _j.bassEndIndex, (_k !== null && _k !== void 0 ? _k : exports.DEFAULT_BASS_END_INDEX));
|
|
46
|
-
var energyHistory = [];
|
|
47
|
-
var framesSinceLastBeat = cooldownFrames;
|
|
48
|
-
var decayingIntensity = 0;
|
|
49
|
-
return function (spectrum) {
|
|
50
|
-
var energy = exports.frameBassEnergy(spectrum, bassEndIndex);
|
|
51
|
-
energyHistory.push(energy);
|
|
52
|
-
if (energyHistory.length > historySize) {
|
|
53
|
-
energyHistory.shift();
|
|
54
|
-
}
|
|
55
|
-
var avgEnergy = energyHistory.reduce(function (sum, e) { return sum + e; }, 0) / energyHistory.length;
|
|
56
|
-
var variance = energyHistory.reduce(function (sum, e) { return sum + Math.pow((e - avgEnergy), 2); }, 0) / energyHistory.length;
|
|
57
|
-
var stdDev = Math.sqrt(variance);
|
|
58
|
-
var threshold = avgEnergy + stdDev * thresholdMultiplier;
|
|
59
|
-
framesSinceLastBeat++;
|
|
60
|
-
var hasEnoughHistory = energyHistory.length >= Math.round(historySize / 3);
|
|
61
|
-
var isAboveThreshold = energy > threshold && hasEnoughHistory;
|
|
62
|
-
var isCooldownOver = framesSinceLastBeat >= cooldownFrames;
|
|
63
|
-
var isBeat = isAboveThreshold && isCooldownOver;
|
|
64
|
-
if (isBeat) {
|
|
65
|
-
framesSinceLastBeat = 0;
|
|
66
|
-
decayingIntensity = Math.min(1, (energy - threshold) / (stdDev + 1e-6));
|
|
67
|
-
}
|
|
68
|
-
else {
|
|
69
|
-
decayingIntensity *= decayRate;
|
|
70
|
-
}
|
|
71
|
-
return {
|
|
72
|
-
isBeat: isBeat,
|
|
73
|
-
intensity: decayingIntensity,
|
|
74
|
-
energy: energy,
|
|
75
|
-
threshold: threshold,
|
|
76
|
-
};
|
|
77
|
-
};
|
|
78
|
-
};
|
|
79
|
-
var foldTempoBpm = function (bpm) {
|
|
80
|
-
var folded = bpm;
|
|
81
|
-
while (folded > exports.MAX_TEMPO_BPM && folded / 2 >= exports.MIN_TEMPO_BPM) {
|
|
82
|
-
folded /= 2;
|
|
83
|
-
}
|
|
84
|
-
while (folded < exports.MIN_TEMPO_BPM && folded * 2 <= exports.MAX_TEMPO_BPM) {
|
|
85
|
-
folded *= 2;
|
|
86
|
-
}
|
|
87
|
-
return folded;
|
|
88
|
-
};
|
|
89
|
-
var samplesToArray = function (samples, maxLength) {
|
|
90
|
-
var n = Math.min(samples.length, maxLength);
|
|
91
|
-
if (Array.isArray(samples) && n === samples.length) {
|
|
92
|
-
return samples;
|
|
93
|
-
}
|
|
94
|
-
var audioData = new Array(n);
|
|
95
|
-
for (var i = 0; i < n; i++) {
|
|
96
|
-
audioData[i] = samples[i];
|
|
97
|
-
}
|
|
98
|
-
return audioData;
|
|
99
|
-
};
|
|
100
|
-
var wrapIntoPeriod = function (value, period) {
|
|
101
|
-
if (!(period > 0) || !isFinite(value)) {
|
|
102
|
-
return 0;
|
|
103
|
-
}
|
|
104
|
-
var wrapped = value - period * Math.floor(value / period);
|
|
105
|
-
if (wrapped < 0) {
|
|
106
|
-
wrapped += period;
|
|
107
|
-
}
|
|
108
|
-
if (wrapped >= period) {
|
|
109
|
-
wrapped = 0;
|
|
110
|
-
}
|
|
111
|
-
return wrapped;
|
|
112
|
-
};
|
|
113
|
-
var circularMeanPhase = function (frames, period) {
|
|
114
|
-
if (!(period > 0) || frames.length === 0) {
|
|
115
|
-
return 0;
|
|
116
|
-
}
|
|
117
|
-
var sinSum = 0;
|
|
118
|
-
var cosSum = 0;
|
|
119
|
-
for (var _i = 0, frames_1 = frames; _i < frames_1.length; _i++) {
|
|
120
|
-
var frame = frames_1[_i];
|
|
121
|
-
var angle = (2 * Math.PI * frame) / period;
|
|
122
|
-
sinSum += Math.sin(angle);
|
|
123
|
-
cosSum += Math.cos(angle);
|
|
124
|
-
}
|
|
125
|
-
var phase = Math.atan2(sinSum, cosSum) * period / (2 * Math.PI);
|
|
126
|
-
return wrapIntoPeriod(phase, period);
|
|
127
|
-
};
|
|
128
|
-
exports.estimateTempo = function (samples, sampleRate, fps) {
|
|
129
|
-
var _a;
|
|
130
|
-
if (!samples || samples.length === 0 || !(sampleRate > 0) || !(fps > 0)) {
|
|
131
|
-
return null;
|
|
132
|
-
}
|
|
133
|
-
var maxSamples = Math.max(1, Math.floor(sampleRate * exports.MAX_TEMPO_ANALYSIS_SECONDS));
|
|
134
|
-
var audioData = samplesToArray(samples, maxSamples);
|
|
135
|
-
var hopSize = Math.max(1, Math.round(sampleRate * TEMPO_TIME_STEP));
|
|
136
|
-
try {
|
|
137
|
-
var mt = new music_tempo_1.default(audioData, {
|
|
138
|
-
hopSize: hopSize,
|
|
139
|
-
timeStep: TEMPO_TIME_STEP,
|
|
140
|
-
minBeatInterval: 60 / exports.MAX_TEMPO_BPM,
|
|
141
|
-
maxBeatInterval: 60 / exports.MIN_TEMPO_BPM,
|
|
142
|
-
});
|
|
143
|
-
var beatInterval = Number(mt.beatInterval);
|
|
144
|
-
var bpm = Number(mt.tempo);
|
|
145
|
-
if (!(bpm > 0) && beatInterval > 0) {
|
|
146
|
-
bpm = 60 / beatInterval;
|
|
147
|
-
}
|
|
148
|
-
if (!(bpm > 0)) {
|
|
149
|
-
return null;
|
|
150
|
-
}
|
|
151
|
-
bpm = foldTempoBpm(bpm);
|
|
152
|
-
if (bpm < exports.MIN_TEMPO_BPM * 0.9 || bpm > exports.MAX_TEMPO_BPM * 1.1) {
|
|
153
|
-
return null;
|
|
154
|
-
}
|
|
155
|
-
var periodFrames = fps * 60 / bpm;
|
|
156
|
-
var beatsSec = (_a = mt.beats, (_a !== null && _a !== void 0 ? _a : []))
|
|
157
|
-
.map(function (t) { return Number(t); })
|
|
158
|
-
.filter(function (t) { return t >= 0 && isFinite(t); });
|
|
159
|
-
var beatFrames = beatsSec.map(function (t) { return t * fps; });
|
|
160
|
-
var phaseFrame = beatFrames.length > 0
|
|
161
|
-
? wrapIntoPeriod(circularMeanPhase(beatFrames, periodFrames), periodFrames)
|
|
162
|
-
: 0;
|
|
163
|
-
return __assign({ bpm: Math.round(bpm), periodFrames: periodFrames,
|
|
164
|
-
phaseFrame: phaseFrame }, (beatsSec.length > 0 ? { beatsSec: beatsSec } : {}));
|
|
165
|
-
}
|
|
166
|
-
catch (_b) {
|
|
167
|
-
return null;
|
|
168
|
-
}
|
|
169
|
-
};
|
|
170
|
-
exports.shiftTempoPhase = function (tempo, startFrame) {
|
|
171
|
-
var period = tempo.periodFrames;
|
|
172
|
-
if (!(period > 0) || startFrame === 0) {
|
|
173
|
-
return tempo;
|
|
174
|
-
}
|
|
175
|
-
return __assign(__assign({}, tempo), { phaseFrame: wrapIntoPeriod(tempo.phaseFrame - startFrame, period) });
|
|
176
|
-
};
|
|
177
|
-
/** Beat-grid phase for a later audio window, using in-window beat times when available. */
|
|
178
|
-
exports.tempoForWindow = function (tempo, startFrame, fps, windowFrames, localOnsetFrames) {
|
|
179
|
-
var _a;
|
|
180
|
-
var period = tempo.periodFrames;
|
|
181
|
-
if (!(period > 0) || !(fps > 0)) {
|
|
182
|
-
return tempo;
|
|
183
|
-
}
|
|
184
|
-
var startSec = startFrame / fps;
|
|
185
|
-
var endSec = windowFrames != null && windowFrames >= 0
|
|
186
|
-
? (startFrame + windowFrames) / fps
|
|
187
|
-
: Number.POSITIVE_INFINITY;
|
|
188
|
-
var inWindow = (_a = tempo.beatsSec, (_a !== null && _a !== void 0 ? _a : [])).filter(function (t) { return t >= startSec && t < endSec; });
|
|
189
|
-
if (inWindow.length > 0) {
|
|
190
|
-
var localFrames = inWindow.map(function (t) { return (t - startSec) * fps; });
|
|
191
|
-
return __assign(__assign({}, tempo), { phaseFrame: wrapIntoPeriod(circularMeanPhase(localFrames, period), period) });
|
|
192
|
-
}
|
|
193
|
-
var onsets = ((localOnsetFrames !== null && localOnsetFrames !== void 0 ? localOnsetFrames : [])).filter(function (frame) {
|
|
194
|
-
return frame > 0 && (windowFrames == null || frame < windowFrames);
|
|
195
|
-
});
|
|
196
|
-
if (onsets.length > 0) {
|
|
197
|
-
return __assign(__assign({}, tempo), { phaseFrame: wrapIntoPeriod(circularMeanPhase(onsets, period), period) });
|
|
198
|
-
}
|
|
199
|
-
return exports.shiftTempoPhase(tempo, startFrame);
|
|
200
|
-
};
|
|
201
|
-
exports.beatGridFrameIndices = function (tempo, totalFrames, stride) {
|
|
202
|
-
if (stride === void 0) { stride = 1; }
|
|
203
|
-
var period = tempo.periodFrames;
|
|
204
|
-
var step = Math.max(1, Math.round(stride));
|
|
205
|
-
if (!(period > 0) || totalFrames <= 1) {
|
|
206
|
-
return [];
|
|
207
|
-
}
|
|
208
|
-
var frames = [];
|
|
209
|
-
var nStart = tempo.phaseFrame > 0 ? 0 : 1;
|
|
210
|
-
for (var n = nStart;; n += step) {
|
|
211
|
-
var frame = Math.round(tempo.phaseFrame + n * period);
|
|
212
|
-
if (frame >= totalFrames) {
|
|
213
|
-
break;
|
|
214
|
-
}
|
|
215
|
-
if (frame > 0) {
|
|
216
|
-
frames.push(frame);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
return frames;
|
|
220
|
-
};
|
|
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 __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
var music_tempo_1 = __importDefault(require("music-tempo"));
|
|
18
|
+
exports.MIN_TEMPO_BPM = 70;
|
|
19
|
+
exports.MAX_TEMPO_BPM = 180;
|
|
20
|
+
exports.PREFERRED_TEMPO_BPM_MIN = 90;
|
|
21
|
+
exports.PREFERRED_TEMPO_BPM_MAX = 160;
|
|
22
|
+
exports.DEFAULT_BASS_END_INDEX = 8;
|
|
23
|
+
/** Longest PCM window passed to music-tempo (seconds), taken from the start of the audio. */
|
|
24
|
+
exports.MAX_TEMPO_ANALYSIS_SECONDS = 90;
|
|
25
|
+
var TEMPO_TIME_STEP = 0.01;
|
|
26
|
+
exports.frameBassEnergy = function (spectrum, bassEndIndex) {
|
|
27
|
+
if (bassEndIndex === void 0) { bassEndIndex = exports.DEFAULT_BASS_END_INDEX; }
|
|
28
|
+
var end = Math.min(bassEndIndex, spectrum.length);
|
|
29
|
+
if (end <= 0) {
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
var sum = 0;
|
|
33
|
+
for (var i = 0; i < end; i++) {
|
|
34
|
+
var v = spectrum[i];
|
|
35
|
+
sum += v * v;
|
|
36
|
+
}
|
|
37
|
+
return sum / end;
|
|
38
|
+
};
|
|
39
|
+
exports.createBeatDetector = function (fps, options) {
|
|
40
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
41
|
+
var historySize = (_b = (_a = options) === null || _a === void 0 ? void 0 : _a.historySize, (_b !== null && _b !== void 0 ? _b : Math.round(fps * 1.5)));
|
|
42
|
+
var thresholdMultiplier = (_d = (_c = options) === null || _c === void 0 ? void 0 : _c.thresholdMultiplier, (_d !== null && _d !== void 0 ? _d : 1.4));
|
|
43
|
+
var decayRate = (_f = (_e = options) === null || _e === void 0 ? void 0 : _e.decayRate, (_f !== null && _f !== void 0 ? _f : 0.85));
|
|
44
|
+
var cooldownFrames = (_h = (_g = options) === null || _g === void 0 ? void 0 : _g.cooldownFrames, (_h !== null && _h !== void 0 ? _h : Math.round(fps * 0.15)));
|
|
45
|
+
var bassEndIndex = (_k = (_j = options) === null || _j === void 0 ? void 0 : _j.bassEndIndex, (_k !== null && _k !== void 0 ? _k : exports.DEFAULT_BASS_END_INDEX));
|
|
46
|
+
var energyHistory = [];
|
|
47
|
+
var framesSinceLastBeat = cooldownFrames;
|
|
48
|
+
var decayingIntensity = 0;
|
|
49
|
+
return function (spectrum) {
|
|
50
|
+
var energy = exports.frameBassEnergy(spectrum, bassEndIndex);
|
|
51
|
+
energyHistory.push(energy);
|
|
52
|
+
if (energyHistory.length > historySize) {
|
|
53
|
+
energyHistory.shift();
|
|
54
|
+
}
|
|
55
|
+
var avgEnergy = energyHistory.reduce(function (sum, e) { return sum + e; }, 0) / energyHistory.length;
|
|
56
|
+
var variance = energyHistory.reduce(function (sum, e) { return sum + Math.pow((e - avgEnergy), 2); }, 0) / energyHistory.length;
|
|
57
|
+
var stdDev = Math.sqrt(variance);
|
|
58
|
+
var threshold = avgEnergy + stdDev * thresholdMultiplier;
|
|
59
|
+
framesSinceLastBeat++;
|
|
60
|
+
var hasEnoughHistory = energyHistory.length >= Math.round(historySize / 3);
|
|
61
|
+
var isAboveThreshold = energy > threshold && hasEnoughHistory;
|
|
62
|
+
var isCooldownOver = framesSinceLastBeat >= cooldownFrames;
|
|
63
|
+
var isBeat = isAboveThreshold && isCooldownOver;
|
|
64
|
+
if (isBeat) {
|
|
65
|
+
framesSinceLastBeat = 0;
|
|
66
|
+
decayingIntensity = Math.min(1, (energy - threshold) / (stdDev + 1e-6));
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
decayingIntensity *= decayRate;
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
isBeat: isBeat,
|
|
73
|
+
intensity: decayingIntensity,
|
|
74
|
+
energy: energy,
|
|
75
|
+
threshold: threshold,
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
var foldTempoBpm = function (bpm) {
|
|
80
|
+
var folded = bpm;
|
|
81
|
+
while (folded > exports.MAX_TEMPO_BPM && folded / 2 >= exports.MIN_TEMPO_BPM) {
|
|
82
|
+
folded /= 2;
|
|
83
|
+
}
|
|
84
|
+
while (folded < exports.MIN_TEMPO_BPM && folded * 2 <= exports.MAX_TEMPO_BPM) {
|
|
85
|
+
folded *= 2;
|
|
86
|
+
}
|
|
87
|
+
return folded;
|
|
88
|
+
};
|
|
89
|
+
var samplesToArray = function (samples, maxLength) {
|
|
90
|
+
var n = Math.min(samples.length, maxLength);
|
|
91
|
+
if (Array.isArray(samples) && n === samples.length) {
|
|
92
|
+
return samples;
|
|
93
|
+
}
|
|
94
|
+
var audioData = new Array(n);
|
|
95
|
+
for (var i = 0; i < n; i++) {
|
|
96
|
+
audioData[i] = samples[i];
|
|
97
|
+
}
|
|
98
|
+
return audioData;
|
|
99
|
+
};
|
|
100
|
+
var wrapIntoPeriod = function (value, period) {
|
|
101
|
+
if (!(period > 0) || !isFinite(value)) {
|
|
102
|
+
return 0;
|
|
103
|
+
}
|
|
104
|
+
var wrapped = value - period * Math.floor(value / period);
|
|
105
|
+
if (wrapped < 0) {
|
|
106
|
+
wrapped += period;
|
|
107
|
+
}
|
|
108
|
+
if (wrapped >= period) {
|
|
109
|
+
wrapped = 0;
|
|
110
|
+
}
|
|
111
|
+
return wrapped;
|
|
112
|
+
};
|
|
113
|
+
var circularMeanPhase = function (frames, period) {
|
|
114
|
+
if (!(period > 0) || frames.length === 0) {
|
|
115
|
+
return 0;
|
|
116
|
+
}
|
|
117
|
+
var sinSum = 0;
|
|
118
|
+
var cosSum = 0;
|
|
119
|
+
for (var _i = 0, frames_1 = frames; _i < frames_1.length; _i++) {
|
|
120
|
+
var frame = frames_1[_i];
|
|
121
|
+
var angle = (2 * Math.PI * frame) / period;
|
|
122
|
+
sinSum += Math.sin(angle);
|
|
123
|
+
cosSum += Math.cos(angle);
|
|
124
|
+
}
|
|
125
|
+
var phase = Math.atan2(sinSum, cosSum) * period / (2 * Math.PI);
|
|
126
|
+
return wrapIntoPeriod(phase, period);
|
|
127
|
+
};
|
|
128
|
+
exports.estimateTempo = function (samples, sampleRate, fps) {
|
|
129
|
+
var _a;
|
|
130
|
+
if (!samples || samples.length === 0 || !(sampleRate > 0) || !(fps > 0)) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
var maxSamples = Math.max(1, Math.floor(sampleRate * exports.MAX_TEMPO_ANALYSIS_SECONDS));
|
|
134
|
+
var audioData = samplesToArray(samples, maxSamples);
|
|
135
|
+
var hopSize = Math.max(1, Math.round(sampleRate * TEMPO_TIME_STEP));
|
|
136
|
+
try {
|
|
137
|
+
var mt = new music_tempo_1.default(audioData, {
|
|
138
|
+
hopSize: hopSize,
|
|
139
|
+
timeStep: TEMPO_TIME_STEP,
|
|
140
|
+
minBeatInterval: 60 / exports.MAX_TEMPO_BPM,
|
|
141
|
+
maxBeatInterval: 60 / exports.MIN_TEMPO_BPM,
|
|
142
|
+
});
|
|
143
|
+
var beatInterval = Number(mt.beatInterval);
|
|
144
|
+
var bpm = Number(mt.tempo);
|
|
145
|
+
if (!(bpm > 0) && beatInterval > 0) {
|
|
146
|
+
bpm = 60 / beatInterval;
|
|
147
|
+
}
|
|
148
|
+
if (!(bpm > 0)) {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
bpm = foldTempoBpm(bpm);
|
|
152
|
+
if (bpm < exports.MIN_TEMPO_BPM * 0.9 || bpm > exports.MAX_TEMPO_BPM * 1.1) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
var periodFrames = fps * 60 / bpm;
|
|
156
|
+
var beatsSec = (_a = mt.beats, (_a !== null && _a !== void 0 ? _a : []))
|
|
157
|
+
.map(function (t) { return Number(t); })
|
|
158
|
+
.filter(function (t) { return t >= 0 && isFinite(t); });
|
|
159
|
+
var beatFrames = beatsSec.map(function (t) { return t * fps; });
|
|
160
|
+
var phaseFrame = beatFrames.length > 0
|
|
161
|
+
? wrapIntoPeriod(circularMeanPhase(beatFrames, periodFrames), periodFrames)
|
|
162
|
+
: 0;
|
|
163
|
+
return __assign({ bpm: Math.round(bpm), periodFrames: periodFrames,
|
|
164
|
+
phaseFrame: phaseFrame }, (beatsSec.length > 0 ? { beatsSec: beatsSec } : {}));
|
|
165
|
+
}
|
|
166
|
+
catch (_b) {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
exports.shiftTempoPhase = function (tempo, startFrame) {
|
|
171
|
+
var period = tempo.periodFrames;
|
|
172
|
+
if (!(period > 0) || startFrame === 0) {
|
|
173
|
+
return tempo;
|
|
174
|
+
}
|
|
175
|
+
return __assign(__assign({}, tempo), { phaseFrame: wrapIntoPeriod(tempo.phaseFrame - startFrame, period) });
|
|
176
|
+
};
|
|
177
|
+
/** Beat-grid phase for a later audio window, using in-window beat times when available. */
|
|
178
|
+
exports.tempoForWindow = function (tempo, startFrame, fps, windowFrames, localOnsetFrames) {
|
|
179
|
+
var _a;
|
|
180
|
+
var period = tempo.periodFrames;
|
|
181
|
+
if (!(period > 0) || !(fps > 0)) {
|
|
182
|
+
return tempo;
|
|
183
|
+
}
|
|
184
|
+
var startSec = startFrame / fps;
|
|
185
|
+
var endSec = windowFrames != null && windowFrames >= 0
|
|
186
|
+
? (startFrame + windowFrames) / fps
|
|
187
|
+
: Number.POSITIVE_INFINITY;
|
|
188
|
+
var inWindow = (_a = tempo.beatsSec, (_a !== null && _a !== void 0 ? _a : [])).filter(function (t) { return t >= startSec && t < endSec; });
|
|
189
|
+
if (inWindow.length > 0) {
|
|
190
|
+
var localFrames = inWindow.map(function (t) { return (t - startSec) * fps; });
|
|
191
|
+
return __assign(__assign({}, tempo), { phaseFrame: wrapIntoPeriod(circularMeanPhase(localFrames, period), period) });
|
|
192
|
+
}
|
|
193
|
+
var onsets = ((localOnsetFrames !== null && localOnsetFrames !== void 0 ? localOnsetFrames : [])).filter(function (frame) {
|
|
194
|
+
return frame > 0 && (windowFrames == null || frame < windowFrames);
|
|
195
|
+
});
|
|
196
|
+
if (onsets.length > 0) {
|
|
197
|
+
return __assign(__assign({}, tempo), { phaseFrame: wrapIntoPeriod(circularMeanPhase(onsets, period), period) });
|
|
198
|
+
}
|
|
199
|
+
return exports.shiftTempoPhase(tempo, startFrame);
|
|
200
|
+
};
|
|
201
|
+
exports.beatGridFrameIndices = function (tempo, totalFrames, stride) {
|
|
202
|
+
if (stride === void 0) { stride = 1; }
|
|
203
|
+
var period = tempo.periodFrames;
|
|
204
|
+
var step = Math.max(1, Math.round(stride));
|
|
205
|
+
if (!(period > 0) || totalFrames <= 1) {
|
|
206
|
+
return [];
|
|
207
|
+
}
|
|
208
|
+
var frames = [];
|
|
209
|
+
var nStart = tempo.phaseFrame > 0 ? 0 : 1;
|
|
210
|
+
for (var n = nStart;; n += step) {
|
|
211
|
+
var frame = Math.round(tempo.phaseFrame + n * period);
|
|
212
|
+
if (frame >= totalFrames) {
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
if (frame > 0) {
|
|
216
|
+
frames.push(frame);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return frames;
|
|
220
|
+
};
|
|
221
221
|
//# sourceMappingURL=beats.js.map
|
package/dist/bpmEncoder.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
export interface EncodedBmp {
|
|
3
|
-
shiftPos: number;
|
|
4
|
-
rowBytes: number;
|
|
5
|
-
data: Buffer;
|
|
6
|
-
}
|
|
7
|
-
export declare const createBpmEncoder: (imgData: {
|
|
8
|
-
width: number;
|
|
9
|
-
height: number;
|
|
10
|
-
}) => (buffer: Buffer) => EncodedBmp;
|
|
11
|
-
export declare const createBgrFrameEncoder: (imgData: {
|
|
12
|
-
width: number;
|
|
13
|
-
height: number;
|
|
14
|
-
}) => (bgrBuffer: Buffer) => EncodedBmp;
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
export interface EncodedBmp {
|
|
3
|
+
shiftPos: number;
|
|
4
|
+
rowBytes: number;
|
|
5
|
+
data: Buffer;
|
|
6
|
+
}
|
|
7
|
+
export declare const createBpmEncoder: (imgData: {
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
}) => (buffer: Buffer) => EncodedBmp;
|
|
11
|
+
export declare const createBgrFrameEncoder: (imgData: {
|
|
12
|
+
width: number;
|
|
13
|
+
height: number;
|
|
14
|
+
}) => (bgrBuffer: Buffer) => EncodedBmp;
|
package/dist/bpmEncoder.js
CHANGED
|
@@ -1,98 +1,98 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
var writeBmpHeader = function (buffer, width, height, rgbSize) {
|
|
4
|
-
var headerInfoSize = 40;
|
|
5
|
-
var offset = 54;
|
|
6
|
-
var fileSize = rgbSize + offset;
|
|
7
|
-
var pos = 0;
|
|
8
|
-
buffer.write('BM', pos, 2);
|
|
9
|
-
pos += 2;
|
|
10
|
-
buffer.writeUInt32LE(fileSize, pos);
|
|
11
|
-
pos += 4;
|
|
12
|
-
buffer.writeUInt32LE(0, pos);
|
|
13
|
-
pos += 4;
|
|
14
|
-
buffer.writeUInt32LE(offset, pos);
|
|
15
|
-
pos += 4;
|
|
16
|
-
buffer.writeUInt32LE(headerInfoSize, pos);
|
|
17
|
-
pos += 4;
|
|
18
|
-
buffer.writeUInt32LE(width, pos);
|
|
19
|
-
pos += 4;
|
|
20
|
-
buffer.writeInt32LE(-height, pos);
|
|
21
|
-
pos += 4;
|
|
22
|
-
buffer.writeUInt16LE(1, pos);
|
|
23
|
-
pos += 2;
|
|
24
|
-
buffer.writeUInt16LE(24, pos);
|
|
25
|
-
pos += 2;
|
|
26
|
-
buffer.writeUInt32LE(0, pos);
|
|
27
|
-
pos += 4;
|
|
28
|
-
buffer.writeUInt32LE(rgbSize, pos);
|
|
29
|
-
pos += 4;
|
|
30
|
-
buffer.writeUInt32LE(0, pos);
|
|
31
|
-
pos += 4;
|
|
32
|
-
buffer.writeUInt32LE(0, pos);
|
|
33
|
-
pos += 4;
|
|
34
|
-
buffer.writeUInt32LE(0, pos);
|
|
35
|
-
pos += 4;
|
|
36
|
-
buffer.writeUInt32LE(0, pos);
|
|
37
|
-
pos += 4;
|
|
38
|
-
return offset;
|
|
39
|
-
};
|
|
40
|
-
exports.createBpmEncoder = function (imgData) {
|
|
41
|
-
var width = imgData.width;
|
|
42
|
-
var height = imgData.height;
|
|
43
|
-
var extraBytes = width % 4;
|
|
44
|
-
var rgbSize = height * (3 * width + extraBytes);
|
|
45
|
-
var offset = 54;
|
|
46
|
-
return function (buffer) {
|
|
47
|
-
var tempBuffer = Buffer.alloc(offset + rgbSize);
|
|
48
|
-
var pos = writeBmpHeader(tempBuffer, width, height, rgbSize);
|
|
49
|
-
var i = 0;
|
|
50
|
-
var rowBytes = 3 * width + extraBytes;
|
|
51
|
-
for (var y = 0; y < height; y++) {
|
|
52
|
-
for (var x = 0; x < width; x++) {
|
|
53
|
-
var p = pos + y * rowBytes + x * 3;
|
|
54
|
-
i++; // a
|
|
55
|
-
tempBuffer[p] = buffer[i++]; // b
|
|
56
|
-
tempBuffer[p + 1] = buffer[i++]; // g
|
|
57
|
-
tempBuffer[p + 2] = buffer[i++]; // r
|
|
58
|
-
}
|
|
59
|
-
if (extraBytes > 0) {
|
|
60
|
-
var fillOffset = pos + y * rowBytes + width * 3;
|
|
61
|
-
tempBuffer.fill(0, fillOffset, fillOffset + extraBytes);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return {
|
|
65
|
-
shiftPos: pos,
|
|
66
|
-
rowBytes: rowBytes,
|
|
67
|
-
data: tempBuffer
|
|
68
|
-
};
|
|
69
|
-
};
|
|
70
|
-
};
|
|
71
|
-
exports.createBgrFrameEncoder = function (imgData) {
|
|
72
|
-
var width = imgData.width;
|
|
73
|
-
var height = imgData.height;
|
|
74
|
-
var extraBytes = width % 4;
|
|
75
|
-
var rowBytes = 3 * width + extraBytes;
|
|
76
|
-
var rgbSize = height * rowBytes;
|
|
77
|
-
var offset = 54;
|
|
78
|
-
var tempBuffer = Buffer.alloc(offset + rgbSize);
|
|
79
|
-
var pos = writeBmpHeader(tempBuffer, width, height, rgbSize);
|
|
80
|
-
return function (bgrBuffer) {
|
|
81
|
-
if (extraBytes === 0) {
|
|
82
|
-
bgrBuffer.copy(tempBuffer, pos, 0, width * height * 3);
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
for (var y = 0; y < height; y++) {
|
|
86
|
-
var srcOffset = y * width * 3;
|
|
87
|
-
var dstOffset = pos + y * rowBytes;
|
|
88
|
-
bgrBuffer.copy(tempBuffer, dstOffset, srcOffset, srcOffset + width * 3);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
return {
|
|
92
|
-
shiftPos: pos,
|
|
93
|
-
rowBytes: rowBytes,
|
|
94
|
-
data: tempBuffer
|
|
95
|
-
};
|
|
96
|
-
};
|
|
97
|
-
};
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
var writeBmpHeader = function (buffer, width, height, rgbSize) {
|
|
4
|
+
var headerInfoSize = 40;
|
|
5
|
+
var offset = 54;
|
|
6
|
+
var fileSize = rgbSize + offset;
|
|
7
|
+
var pos = 0;
|
|
8
|
+
buffer.write('BM', pos, 2);
|
|
9
|
+
pos += 2;
|
|
10
|
+
buffer.writeUInt32LE(fileSize, pos);
|
|
11
|
+
pos += 4;
|
|
12
|
+
buffer.writeUInt32LE(0, pos);
|
|
13
|
+
pos += 4;
|
|
14
|
+
buffer.writeUInt32LE(offset, pos);
|
|
15
|
+
pos += 4;
|
|
16
|
+
buffer.writeUInt32LE(headerInfoSize, pos);
|
|
17
|
+
pos += 4;
|
|
18
|
+
buffer.writeUInt32LE(width, pos);
|
|
19
|
+
pos += 4;
|
|
20
|
+
buffer.writeInt32LE(-height, pos);
|
|
21
|
+
pos += 4;
|
|
22
|
+
buffer.writeUInt16LE(1, pos);
|
|
23
|
+
pos += 2;
|
|
24
|
+
buffer.writeUInt16LE(24, pos);
|
|
25
|
+
pos += 2;
|
|
26
|
+
buffer.writeUInt32LE(0, pos);
|
|
27
|
+
pos += 4;
|
|
28
|
+
buffer.writeUInt32LE(rgbSize, pos);
|
|
29
|
+
pos += 4;
|
|
30
|
+
buffer.writeUInt32LE(0, pos);
|
|
31
|
+
pos += 4;
|
|
32
|
+
buffer.writeUInt32LE(0, pos);
|
|
33
|
+
pos += 4;
|
|
34
|
+
buffer.writeUInt32LE(0, pos);
|
|
35
|
+
pos += 4;
|
|
36
|
+
buffer.writeUInt32LE(0, pos);
|
|
37
|
+
pos += 4;
|
|
38
|
+
return offset;
|
|
39
|
+
};
|
|
40
|
+
exports.createBpmEncoder = function (imgData) {
|
|
41
|
+
var width = imgData.width;
|
|
42
|
+
var height = imgData.height;
|
|
43
|
+
var extraBytes = width % 4;
|
|
44
|
+
var rgbSize = height * (3 * width + extraBytes);
|
|
45
|
+
var offset = 54;
|
|
46
|
+
return function (buffer) {
|
|
47
|
+
var tempBuffer = Buffer.alloc(offset + rgbSize);
|
|
48
|
+
var pos = writeBmpHeader(tempBuffer, width, height, rgbSize);
|
|
49
|
+
var i = 0;
|
|
50
|
+
var rowBytes = 3 * width + extraBytes;
|
|
51
|
+
for (var y = 0; y < height; y++) {
|
|
52
|
+
for (var x = 0; x < width; x++) {
|
|
53
|
+
var p = pos + y * rowBytes + x * 3;
|
|
54
|
+
i++; // a
|
|
55
|
+
tempBuffer[p] = buffer[i++]; // b
|
|
56
|
+
tempBuffer[p + 1] = buffer[i++]; // g
|
|
57
|
+
tempBuffer[p + 2] = buffer[i++]; // r
|
|
58
|
+
}
|
|
59
|
+
if (extraBytes > 0) {
|
|
60
|
+
var fillOffset = pos + y * rowBytes + width * 3;
|
|
61
|
+
tempBuffer.fill(0, fillOffset, fillOffset + extraBytes);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
shiftPos: pos,
|
|
66
|
+
rowBytes: rowBytes,
|
|
67
|
+
data: tempBuffer
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
exports.createBgrFrameEncoder = function (imgData) {
|
|
72
|
+
var width = imgData.width;
|
|
73
|
+
var height = imgData.height;
|
|
74
|
+
var extraBytes = width % 4;
|
|
75
|
+
var rowBytes = 3 * width + extraBytes;
|
|
76
|
+
var rgbSize = height * rowBytes;
|
|
77
|
+
var offset = 54;
|
|
78
|
+
var tempBuffer = Buffer.alloc(offset + rgbSize);
|
|
79
|
+
var pos = writeBmpHeader(tempBuffer, width, height, rgbSize);
|
|
80
|
+
return function (bgrBuffer) {
|
|
81
|
+
if (extraBytes === 0) {
|
|
82
|
+
bgrBuffer.copy(tempBuffer, pos, 0, width * height * 3);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
for (var y = 0; y < height; y++) {
|
|
86
|
+
var srcOffset = y * width * 3;
|
|
87
|
+
var dstOffset = pos + y * rowBytes;
|
|
88
|
+
bgrBuffer.copy(tempBuffer, dstOffset, srcOffset, srcOffset + width * 3);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
shiftPos: pos,
|
|
93
|
+
rowBytes: rowBytes,
|
|
94
|
+
data: tempBuffer
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
98
|
//# sourceMappingURL=bpmEncoder.js.map
|