summaryrefslogtreecommitdiff
path: root/public/js/components/voice-level.js
diff options
context:
space:
mode:
Diffstat (limited to 'public/js/components/voice-level.js')
-rw-r--r--public/js/components/voice-level.js39
1 files changed, 39 insertions, 0 deletions
diff --git a/public/js/components/voice-level.js b/public/js/components/voice-level.js
new file mode 100644
index 0000000..44e9ccc
--- /dev/null
+++ b/public/js/components/voice-level.js
@@ -0,0 +1,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);
+ },
+});