forked from WonderlandEngine/components
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhowler-audio-source.js
92 lines (82 loc) · 2.82 KB
/
howler-audio-source.js
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import {Component, Type} from '@wonderlandengine/api';
import 'howler';
/**
* (Spatial) audio source based on [Howler.js](https://howlerjs.com/).
*
* Creates a Howler audio source, plays an audio file on it and updates
* its position.
*
* Optimizes the position update to only update if the difference to last
* position is larger than half a centimeter. To force updates (e.g. if
* the sound source is _very_ close to the listener),
* use `.updatePosition()`.
*/
export class HowlerAudioSource extends Component {
static TypeName = 'howler-audio-source';
static Properties = {
/** Volume */
volume: {type: Type.Float, default: 1.0},
/** Whether audio should be spatialized/positional */
spatial: {type: Type.Bool, default: true},
/** Whether to loop the sound */
loop: {type: Type.Bool, default: false},
/** Whether to start playing automatically */
autoplay: {type: Type.Bool, default: false},
/** URL to a sound file to play */
src: {type: Type.String, default: ''},
};
start() {
this.audio = new Howl({
src: [this.src],
loop: this.loop,
volume: this.volume,
autoplay: this.autoplay,
});
this.lastPlayedAudioId = null;
this.origin = new Float32Array(3);
this.lastOrigin = new Float32Array(3);
if (this.spatial && this.autoplay) {
this.updatePosition();
this.play();
}
}
update() {
if (!this.spatial || !this.lastPlayedAudioId) return;
this.object.getTranslationWorld(this.origin);
/* Only call pos() if the position moved more than half a centimeter
* otherwise this gets very performance heavy.
* Smaller movement should only be perceivable if close to the
* ear anyway. */
if (
Math.abs(this.lastOrigin[0] - this.origin[0]) > 0.005 ||
Math.abs(this.lastOrigin[1] - this.origin[1]) > 0.005 ||
Math.abs(this.lastOrigin[2] - this.origin[2]) > 0.005
) {
this.updatePosition();
}
}
updatePosition() {
this.audio.pos(
this.origin[0],
this.origin[1],
this.origin[2],
this.lastPlayedAudioId
);
this.lastOrigin.set(this.origin);
}
play() {
if (this.lastPlayedAudioId) this.audio.stop(this.lastPlayedAudioId);
this.lastPlayedAudioId = this.audio.play();
if (this.spatial) this.updatePosition();
}
stop() {
if (!this.lastPlayedAudioId) return;
this.audio.stop(this.lastPlayedAudioId);
this.lastPlayedAudioId = null;
}
onDeactivate() {
/* Stop sound when component is deactivated or destroyed, e.g.
* when switching scenes */
this.stop();
}
}