summaryrefslogtreecommitdiff
path: root/public/js/components/voice-level.js
blob: 44e9ccc058f7cac2d79e764ed8748c3bf561649c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/* global AFRAME */
// Measures how loud a remote person is talking (0..1) from their incoming voice
// stream, for lip-sync and the speaking indicator.
AFRAME.registerComponent('voice-level', {
  init() {
    this.level = 0;
    this.analyser = null;
    this.samples = null;

    this.onSoundSource = (evt) => {
      const source = evt.detail.soundSource;
      if (this.analyser) this.analyser.disconnect();
      this.analyser = source.context.createAnalyser();
      this.analyser.fftSize = 512;
      this.samples = new Uint8Array(this.analyser.fftSize);
      source.connect(this.analyser);
    };
    this.el.addEventListener('sound-source-set', this.onSoundSource);
  },

  remove() {
    this.el.removeEventListener('sound-source-set', this.onSoundSource);
    if (this.analyser) this.analyser.disconnect();
  },

  tick() {
    if (!this.analyser) return;
    this.analyser.getByteTimeDomainData(this.samples);
    let sum = 0;
    for (let i = 0; i < this.samples.length; i++) {
      const v = (this.samples[i] - 128) / 128;
      sum += v * v;
    }
    const rms = Math.sqrt(sum / this.samples.length);
    const target = Math.min(1, Math.max(0, (rms - 0.012) * 9));
    // Open quickly, close a little slower, so mouths don't chatter.
    this.level += (target - this.level) * (target > this.level ? 0.6 : 0.2);
  },
});