forked from Wizcorp/AudioManager
-
Notifications
You must be signed in to change notification settings - Fork 0
/
AudioChannel.js
137 lines (119 loc) · 5.12 KB
/
AudioChannel.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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
/** Audio Channel class.
*
* @author Cedric Stoquer
*
*
* @param {string} id - channel name id
*/
function AudioChannel(id) {
this.id = id;
this.volume = 1.0;
this.muted = true;
this.loopSound = null;
this.loopId = null;
this.loopVol = 0.0;
this.nextLoop = null;
}
module.exports = AudioChannel;
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
AudioChannel.prototype.setVolume = function (volume, muted) {
var wasChannelMuted = this.muted;
this.muted = volume === 0 || muted || false;
if (volume !== undefined && volume !== null) {
this.volume = volume;
} else {
volume = this.volume;
}
if (!this.loopId) return;
// this channel have a looped sound (music, ambient sfx)
// we have to take care of this looped sound playback if channel state changed
if (this.loopSound && this.muted) {
// a sound was playing, channel becomes muted
this.loopSound.stop();
// TODO: unload sound ?
} else if (this.loopSound && this.loopSound.id === this.loopId) {
// correct sound is loaded in channel, updating volume & playback
this.loopSound.setVolume(Math.max(0, Math.min(1, volume * this.loopVol)));
if (wasChannelMuted) { this.loopSound.play(); }
} else if (!this.muted) {
// sound is not loaded in channel, channel has been unmutted
this.audioManager.playLoopSound(this.id, this.loopId, this.loopVol);
}
};
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
AudioChannel.prototype.setMute = function (mute) {
this.setVolume(null, mute);
};
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
/** Play a long looped sound (e.g. background music).
* Only one looped sound can play per channel.
*
* @param {string} soundId - sound id
* @param {number} [volume] - optional volume, a integer in range ]0..1]
* @param {number} [pan] - optional panoramic, a integer in rage [-1..1]
* @param {number} [pitch] - optional pitch, in semi-tone
*/
AudioChannel.prototype.playLoopSound = function (soundId, volume, pan, pitch) {
var audioManager = this.audioManager;
var defaultFade = audioManager.settings.defaultFade;
var crossFading = audioManager.settings.crossFading;
var currentSound = this.loopSound;
var currentSoundId = currentSound && currentSound.id;
volume = Math.max(0, Math.min(1, volume || 1));
this.loopId = soundId;
this.loopVol = volume;
// don't load or play sound if audio or channel is mutted
if (audioManager.muted || this.muted) return;
// if requested sound is already playing, update volume, pan and pitch
if (soundId === currentSoundId && currentSound && (currentSound.playing || currentSound.stopping)) {
currentSound.play(volume * this.volume, pan, pitch);
if (this.nextLoop) {
this.nextLoop.cancelOnLoadCallbacks();
this.nextLoop = null;
}
return;
}
currentSound = null;
// check if requested sound is already scheduled to play next
if (this.nextLoop && this.nextLoop.id === soundId) return;
var self = this;
function stopCurrentLoop(sound, cb) {
if (!sound) return cb && cb();
if (sound.stopping) return; // callback is already scheduled
sound.stop(function () {
audioManager.freeSound(sound); // TODO: add an option to keep file in memory
sound = null;
return cb && cb();
});
}
function _playNextSound() {
var sound = self.loopSound = self.nextLoop;
self.nextLoop = null;
if (!sound) return;
sound.setLoop(true);
sound.fade = defaultFade;
sound.play(volume * self.volume, pan, pitch); // load and play
}
function playNextSound() {
// remove reference to current loop sound to ease optimistic garbabe collection
self.loopSound = null;
// force loading to happen at next tick in order to let garbage collector to release previous audio.
window.setTimeout(_playNextSound, 0);
}
if (crossFading) {
if (this.nextLoop) {
// if another nextSound already loading, cancel previous callback
this.nextLoop.cancelOnLoadCallbacks();
}
this.nextLoop = audioManager.createSound(soundId);
this.nextLoop.load(function onSoundLoad(error) {
if (error) return;
stopCurrentLoop(this.loopSound);
playNextSound();
});
} else {
this.nextLoop = audioManager.createSound(soundId);
stopCurrentLoop(this.loopSound, playNextSound);
}
};