UNPKG

1.76 kBJavaScriptView Raw
1const EventEmitter = require('eventemitter3');
2
3function getAtTime(set, time) {
4 for (let i = 0; i < set.length; i++) {
5 if (time < set[i].start) {
6 return set[i - 1];
7 }
8 }
9}
10
11export class AnalysisResolver {
12 constructor(analysis) {
13 this.analysis = analysis;
14 }
15
16 resolve(atTime) {
17 return {
18 bar: getAtTime(this.analysis.bars, atTime),
19 beat: getAtTime(this.analysis.beats, atTime),
20 section: getAtTime(this.analysis.sections, atTime),
21 segment: getAtTime(this.analysis.segments, atTime),
22 tatum: getAtTime(this.analysis.tatums, atTime),
23 };
24 }
25}
26
27export class AnalysisStream extends EventEmitter {
28 constructor(analyzer) {
29 super();
30
31 this.analyzer = analyzer;
32 this.frameId = null;
33
34 this.offsetTime = null;
35 this.lastTime = 0;
36
37 this.onFrame = this.onFrame.bind(this);
38 }
39
40 run(state) {
41 if (state) {
42 this.start();
43 } else {
44 this.stop();
45 }
46 }
47
48 sync(songPositionMs) {
49 this.offsetTime = this.lastTime - songPositionMs;
50 }
51
52 onFrame(time) {
53 if (!this.offsetTime) {
54 this.offsetTime = time;
55 }
56
57 this.lastTime = time;
58
59 const songPositionMs = time - this.offsetTime;
60 const data = this.analyzer.resolve(songPositionMs / 1000);
61
62 this.emit('data', Object.assign({
63 position: songPositionMs / 1000,
64 }, data));
65
66 this.frameId = window.requestAnimationFrame(this.onFrame);
67 }
68
69 start() {
70 if (this.frameId === null) {
71 this.frameId = window.requestAnimationFrame(this.onFrame);
72 }
73 }
74
75 stop() {
76 window.cancelAnimationFrame(this.frameId);
77 this.frameId = null;
78 }
79}
80
81export function stream(fett) {
82 return new AnalysisStream(new AnalysisResolver(fett));
83}