@ohos-ports/dm-howler 2.2.4-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,657 @@
1
+ /*!
2
+ * Spatial Plugin - Adds support for stereo and 3D audio where Web Audio is supported.
3
+ *
4
+ * howler.js v2.2.3
5
+ * howlerjs.com
6
+ *
7
+ * (c) 2013-2020, James Simpson of GoldFire Studios
8
+ * goldfirestudios.com
9
+ *
10
+ * MIT License
11
+ */
12
+
13
+ export default function(HowlerGlobal, Howler, Howl, Sound) {
14
+
15
+ // Setup default properties.
16
+ HowlerGlobal.prototype._pos = [0, 0, 0];
17
+ HowlerGlobal.prototype._orientation = [0, 0, -1, 0, 1, 0];
18
+
19
+ /** Global Methods **/
20
+ /***************************************************************************/
21
+
22
+ /**
23
+ * Helper method to update the stereo panning position of all current Howls.
24
+ * Future Howls will not use this value unless explicitly set.
25
+ * @param {Number} pan A value of -1.0 is all the way left and 1.0 is all the way right.
26
+ * @return {Howler/Number} Self or current stereo panning value.
27
+ */
28
+ HowlerGlobal.prototype.stereo = function(pan) {
29
+ var self = this;
30
+
31
+ // Stop right here if not using Web Audio.
32
+ if (!self.ctx || !self.ctx.listener) {
33
+ return self;
34
+ }
35
+
36
+ // Loop through all Howls and update their stereo panning.
37
+ for (var i=self._howls.length-1; i>=0; i--) {
38
+ self._howls[i].stereo(pan);
39
+ }
40
+
41
+ return self;
42
+ };
43
+
44
+ /**
45
+ * Get/set the position of the listener in 3D cartesian space. Sounds using
46
+ * 3D position will be relative to the listener's position.
47
+ * @param {Number} x The x-position of the listener.
48
+ * @param {Number} y The y-position of the listener.
49
+ * @param {Number} z The z-position of the listener.
50
+ * @return {Howler/Array} Self or current listener position.
51
+ */
52
+ HowlerGlobal.prototype.pos = function(x, y, z) {
53
+ var self = this;
54
+
55
+ // Stop right here if not using Web Audio.
56
+ if (!self.ctx || !self.ctx.listener) {
57
+ return self;
58
+ }
59
+
60
+ // Set the defaults for optional 'y' & 'z'.
61
+ y = (typeof y !== 'number') ? self._pos[1] : y;
62
+ z = (typeof z !== 'number') ? self._pos[2] : z;
63
+
64
+ if (typeof x === 'number') {
65
+ self._pos = [x, y, z];
66
+
67
+ if (typeof self.ctx.listener.positionX !== 'undefined') {
68
+ self.ctx.listener.positionX.setTargetAtTime(self._pos[0], Howler.ctx.currentTime, 0.1);
69
+ self.ctx.listener.positionY.setTargetAtTime(self._pos[1], Howler.ctx.currentTime, 0.1);
70
+ self.ctx.listener.positionZ.setTargetAtTime(self._pos[2], Howler.ctx.currentTime, 0.1);
71
+ } else {
72
+ self.ctx.listener.setPosition(self._pos[0], self._pos[1], self._pos[2]);
73
+ }
74
+ } else {
75
+ return self._pos;
76
+ }
77
+
78
+ return self;
79
+ };
80
+
81
+ /**
82
+ * Get/set the direction the listener is pointing in the 3D cartesian space.
83
+ * A front and up vector must be provided. The front is the direction the
84
+ * face of the listener is pointing, and up is the direction the top of the
85
+ * listener is pointing. Thus, these values are expected to be at right angles
86
+ * from each other.
87
+ * @param {Number} x The x-orientation of the listener.
88
+ * @param {Number} y The y-orientation of the listener.
89
+ * @param {Number} z The z-orientation of the listener.
90
+ * @param {Number} xUp The x-orientation of the top of the listener.
91
+ * @param {Number} yUp The y-orientation of the top of the listener.
92
+ * @param {Number} zUp The z-orientation of the top of the listener.
93
+ * @return {Howler/Array} Returns self or the current orientation vectors.
94
+ */
95
+ HowlerGlobal.prototype.orientation = function(x, y, z, xUp, yUp, zUp) {
96
+ var self = this;
97
+
98
+ // Stop right here if not using Web Audio.
99
+ if (!self.ctx || !self.ctx.listener) {
100
+ return self;
101
+ }
102
+
103
+ // Set the defaults for optional 'y' & 'z'.
104
+ var or = self._orientation;
105
+ y = (typeof y !== 'number') ? or[1] : y;
106
+ z = (typeof z !== 'number') ? or[2] : z;
107
+ xUp = (typeof xUp !== 'number') ? or[3] : xUp;
108
+ yUp = (typeof yUp !== 'number') ? or[4] : yUp;
109
+ zUp = (typeof zUp !== 'number') ? or[5] : zUp;
110
+
111
+ if (typeof x === 'number') {
112
+ self._orientation = [x, y, z, xUp, yUp, zUp];
113
+
114
+ if (typeof self.ctx.listener.forwardX !== 'undefined') {
115
+ self.ctx.listener.forwardX.setTargetAtTime(x, Howler.ctx.currentTime, 0.1);
116
+ self.ctx.listener.forwardY.setTargetAtTime(y, Howler.ctx.currentTime, 0.1);
117
+ self.ctx.listener.forwardZ.setTargetAtTime(z, Howler.ctx.currentTime, 0.1);
118
+ self.ctx.listener.upX.setTargetAtTime(xUp, Howler.ctx.currentTime, 0.1);
119
+ self.ctx.listener.upY.setTargetAtTime(yUp, Howler.ctx.currentTime, 0.1);
120
+ self.ctx.listener.upZ.setTargetAtTime(zUp, Howler.ctx.currentTime, 0.1);
121
+ } else {
122
+ self.ctx.listener.setOrientation(x, y, z, xUp, yUp, zUp);
123
+ }
124
+ } else {
125
+ return or;
126
+ }
127
+
128
+ return self;
129
+ };
130
+
131
+ /** Group Methods **/
132
+ /***************************************************************************/
133
+
134
+ /**
135
+ * Add new properties to the core init.
136
+ * @param {Function} _super Core init method.
137
+ * @return {Howl}
138
+ */
139
+ Howl.prototype.init = (function(_super) {
140
+ return function(o) {
141
+ var self = this;
142
+
143
+ // Setup user-defined default properties.
144
+ self._orientation = o.orientation || [1, 0, 0];
145
+ self._stereo = o.stereo || null;
146
+ self._pos = o.pos || null;
147
+ self._pannerAttr = {
148
+ coneInnerAngle: typeof o.coneInnerAngle !== 'undefined' ? o.coneInnerAngle : 360,
149
+ coneOuterAngle: typeof o.coneOuterAngle !== 'undefined' ? o.coneOuterAngle : 360,
150
+ coneOuterGain: typeof o.coneOuterGain !== 'undefined' ? o.coneOuterGain : 0,
151
+ distanceModel: typeof o.distanceModel !== 'undefined' ? o.distanceModel : 'inverse',
152
+ maxDistance: typeof o.maxDistance !== 'undefined' ? o.maxDistance : 10000,
153
+ panningModel: typeof o.panningModel !== 'undefined' ? o.panningModel : 'HRTF',
154
+ refDistance: typeof o.refDistance !== 'undefined' ? o.refDistance : 1,
155
+ rolloffFactor: typeof o.rolloffFactor !== 'undefined' ? o.rolloffFactor : 1
156
+ };
157
+
158
+ // Setup event listeners.
159
+ self._onstereo = o.onstereo ? [{fn: o.onstereo}] : [];
160
+ self._onpos = o.onpos ? [{fn: o.onpos}] : [];
161
+ self._onorientation = o.onorientation ? [{fn: o.onorientation}] : [];
162
+
163
+ // Complete initilization with howler.js core's init function.
164
+ return _super.call(this, o);
165
+ };
166
+ })(Howl.prototype.init);
167
+
168
+ /**
169
+ * Get/set the stereo panning of the audio source for this sound or all in the group.
170
+ * @param {Number} pan A value of -1.0 is all the way left and 1.0 is all the way right.
171
+ * @param {Number} id (optional) The sound ID. If none is passed, all in group will be updated.
172
+ * @return {Howl/Number} Returns self or the current stereo panning value.
173
+ */
174
+ Howl.prototype.stereo = function(pan, id) {
175
+ var self = this;
176
+
177
+ // Stop right here if not using Web Audio.
178
+ if (!self._webAudio) {
179
+ return self;
180
+ }
181
+
182
+ // If the sound hasn't loaded, add it to the load queue to change stereo pan when capable.
183
+ if (self._state !== 'loaded') {
184
+ self._queue.push({
185
+ event: 'stereo',
186
+ action: function() {
187
+ self.stereo(pan, id);
188
+ }
189
+ });
190
+
191
+ return self;
192
+ }
193
+
194
+ // Check for PannerStereoNode support and fallback to PannerNode if it doesn't exist.
195
+ var pannerType = (typeof Howler.ctx.createStereoPanner === 'undefined') ? 'spatial' : 'stereo';
196
+
197
+ // Setup the group's stereo panning if no ID is passed.
198
+ if (typeof id === 'undefined') {
199
+ // Return the group's stereo panning if no parameters are passed.
200
+ if (typeof pan === 'number') {
201
+ self._stereo = pan;
202
+ self._pos = [pan, 0, 0];
203
+ } else {
204
+ return self._stereo;
205
+ }
206
+ }
207
+
208
+ // Change the streo panning of one or all sounds in group.
209
+ var ids = self._getSoundIds(id);
210
+ for (var i=0; i<ids.length; i++) {
211
+ // Get the sound.
212
+ var sound = self._soundById(ids[i]);
213
+
214
+ if (sound) {
215
+ if (typeof pan === 'number') {
216
+ sound._stereo = pan;
217
+ sound._pos = [pan, 0, 0];
218
+
219
+ if (sound._node) {
220
+ // If we are falling back, make sure the panningModel is equalpower.
221
+ sound._pannerAttr.panningModel = 'equalpower';
222
+
223
+ // Check if there is a panner setup and create a new one if not.
224
+ if (!sound._panner || !sound._panner.pan) {
225
+ setupPanner(sound, pannerType);
226
+ }
227
+
228
+ if (pannerType === 'spatial') {
229
+ if (typeof sound._panner.positionX !== 'undefined') {
230
+ sound._panner.positionX.setValueAtTime(pan, Howler.ctx.currentTime);
231
+ sound._panner.positionY.setValueAtTime(0, Howler.ctx.currentTime);
232
+ sound._panner.positionZ.setValueAtTime(0, Howler.ctx.currentTime);
233
+ } else {
234
+ sound._panner.setPosition(pan, 0, 0);
235
+ }
236
+ } else {
237
+ sound._panner.pan.setValueAtTime(pan, Howler.ctx.currentTime);
238
+ }
239
+ }
240
+
241
+ self._emit('stereo', sound._id);
242
+ } else {
243
+ return sound._stereo;
244
+ }
245
+ }
246
+ }
247
+
248
+ return self;
249
+ };
250
+
251
+ /**
252
+ * Get/set the 3D spatial position of the audio source for this sound or group relative to the global listener.
253
+ * @param {Number} x The x-position of the audio source.
254
+ * @param {Number} y The y-position of the audio source.
255
+ * @param {Number} z The z-position of the audio source.
256
+ * @param {Number} id (optional) The sound ID. If none is passed, all in group will be updated.
257
+ * @return {Howl/Array} Returns self or the current 3D spatial position: [x, y, z].
258
+ */
259
+ Howl.prototype.pos = function(x, y, z, id) {
260
+ var self = this;
261
+
262
+ // Stop right here if not using Web Audio.
263
+ if (!self._webAudio) {
264
+ return self;
265
+ }
266
+
267
+ // If the sound hasn't loaded, add it to the load queue to change position when capable.
268
+ if (self._state !== 'loaded') {
269
+ self._queue.push({
270
+ event: 'pos',
271
+ action: function() {
272
+ self.pos(x, y, z, id);
273
+ }
274
+ });
275
+
276
+ return self;
277
+ }
278
+
279
+ // Set the defaults for optional 'y' & 'z'.
280
+ y = (typeof y !== 'number') ? 0 : y;
281
+ z = (typeof z !== 'number') ? -0.5 : z;
282
+
283
+ // Setup the group's spatial position if no ID is passed.
284
+ if (typeof id === 'undefined') {
285
+ // Return the group's spatial position if no parameters are passed.
286
+ if (typeof x === 'number') {
287
+ self._pos = [x, y, z];
288
+ } else {
289
+ return self._pos;
290
+ }
291
+ }
292
+
293
+ // Change the spatial position of one or all sounds in group.
294
+ var ids = self._getSoundIds(id);
295
+ for (var i=0; i<ids.length; i++) {
296
+ // Get the sound.
297
+ var sound = self._soundById(ids[i]);
298
+
299
+ if (sound) {
300
+ if (typeof x === 'number') {
301
+ sound._pos = [x, y, z];
302
+
303
+ if (sound._node) {
304
+ // Check if there is a panner setup and create a new one if not.
305
+ if (!sound._panner || sound._panner.pan) {
306
+ setupPanner(sound, 'spatial');
307
+ }
308
+
309
+ if (typeof sound._panner.positionX !== 'undefined') {
310
+ sound._panner.positionX.setValueAtTime(x, Howler.ctx.currentTime);
311
+ sound._panner.positionY.setValueAtTime(y, Howler.ctx.currentTime);
312
+ sound._panner.positionZ.setValueAtTime(z, Howler.ctx.currentTime);
313
+ } else {
314
+ sound._panner.setPosition(x, y, z);
315
+ }
316
+ }
317
+
318
+ self._emit('pos', sound._id);
319
+ } else {
320
+ return sound._pos;
321
+ }
322
+ }
323
+ }
324
+
325
+ return self;
326
+ };
327
+
328
+ /**
329
+ * Get/set the direction the audio source is pointing in the 3D cartesian coordinate
330
+ * space. Depending on how direction the sound is, based on the `cone` attributes,
331
+ * a sound pointing away from the listener can be quiet or silent.
332
+ * @param {Number} x The x-orientation of the source.
333
+ * @param {Number} y The y-orientation of the source.
334
+ * @param {Number} z The z-orientation of the source.
335
+ * @param {Number} id (optional) The sound ID. If none is passed, all in group will be updated.
336
+ * @return {Howl/Array} Returns self or the current 3D spatial orientation: [x, y, z].
337
+ */
338
+ Howl.prototype.orientation = function(x, y, z, id) {
339
+ var self = this;
340
+
341
+ // Stop right here if not using Web Audio.
342
+ if (!self._webAudio) {
343
+ return self;
344
+ }
345
+
346
+ // If the sound hasn't loaded, add it to the load queue to change orientation when capable.
347
+ if (self._state !== 'loaded') {
348
+ self._queue.push({
349
+ event: 'orientation',
350
+ action: function() {
351
+ self.orientation(x, y, z, id);
352
+ }
353
+ });
354
+
355
+ return self;
356
+ }
357
+
358
+ // Set the defaults for optional 'y' & 'z'.
359
+ y = (typeof y !== 'number') ? self._orientation[1] : y;
360
+ z = (typeof z !== 'number') ? self._orientation[2] : z;
361
+
362
+ // Setup the group's spatial orientation if no ID is passed.
363
+ if (typeof id === 'undefined') {
364
+ // Return the group's spatial orientation if no parameters are passed.
365
+ if (typeof x === 'number') {
366
+ self._orientation = [x, y, z];
367
+ } else {
368
+ return self._orientation;
369
+ }
370
+ }
371
+
372
+ // Change the spatial orientation of one or all sounds in group.
373
+ var ids = self._getSoundIds(id);
374
+ for (var i=0; i<ids.length; i++) {
375
+ // Get the sound.
376
+ var sound = self._soundById(ids[i]);
377
+
378
+ if (sound) {
379
+ if (typeof x === 'number') {
380
+ sound._orientation = [x, y, z];
381
+
382
+ if (sound._node) {
383
+ // Check if there is a panner setup and create a new one if not.
384
+ if (!sound._panner) {
385
+ // Make sure we have a position to setup the node with.
386
+ if (!sound._pos) {
387
+ sound._pos = self._pos || [0, 0, -0.5];
388
+ }
389
+
390
+ setupPanner(sound, 'spatial');
391
+ }
392
+
393
+ if (typeof sound._panner.orientationX !== 'undefined') {
394
+ sound._panner.orientationX.setValueAtTime(x, Howler.ctx.currentTime);
395
+ sound._panner.orientationY.setValueAtTime(y, Howler.ctx.currentTime);
396
+ sound._panner.orientationZ.setValueAtTime(z, Howler.ctx.currentTime);
397
+ } else {
398
+ sound._panner.setOrientation(x, y, z);
399
+ }
400
+ }
401
+
402
+ self._emit('orientation', sound._id);
403
+ } else {
404
+ return sound._orientation;
405
+ }
406
+ }
407
+ }
408
+
409
+ return self;
410
+ };
411
+
412
+ /**
413
+ * Get/set the panner node's attributes for a sound or group of sounds.
414
+ * This method can optionall take 0, 1 or 2 arguments.
415
+ * pannerAttr() -> Returns the group's values.
416
+ * pannerAttr(id) -> Returns the sound id's values.
417
+ * pannerAttr(o) -> Set's the values of all sounds in this Howl group.
418
+ * pannerAttr(o, id) -> Set's the values of passed sound id.
419
+ *
420
+ * Attributes:
421
+ * coneInnerAngle - (360 by default) A parameter for directional audio sources, this is an angle, in degrees,
422
+ * inside of which there will be no volume reduction.
423
+ * coneOuterAngle - (360 by default) A parameter for directional audio sources, this is an angle, in degrees,
424
+ * outside of which the volume will be reduced to a constant value of `coneOuterGain`.
425
+ * coneOuterGain - (0 by default) A parameter for directional audio sources, this is the gain outside of the
426
+ * `coneOuterAngle`. It is a linear value in the range `[0, 1]`.
427
+ * distanceModel - ('inverse' by default) Determines algorithm used to reduce volume as audio moves away from
428
+ * listener. Can be `linear`, `inverse` or `exponential.
429
+ * maxDistance - (10000 by default) The maximum distance between source and listener, after which the volume
430
+ * will not be reduced any further.
431
+ * refDistance - (1 by default) A reference distance for reducing volume as source moves further from the listener.
432
+ * This is simply a variable of the distance model and has a different effect depending on which model
433
+ * is used and the scale of your coordinates. Generally, volume will be equal to 1 at this distance.
434
+ * rolloffFactor - (1 by default) How quickly the volume reduces as source moves from listener. This is simply a
435
+ * variable of the distance model and can be in the range of `[0, 1]` with `linear` and `[0, ∞]`
436
+ * with `inverse` and `exponential`.
437
+ * panningModel - ('HRTF' by default) Determines which spatialization algorithm is used to position audio.
438
+ * Can be `HRTF` or `equalpower`.
439
+ *
440
+ * @return {Howl/Object} Returns self or current panner attributes.
441
+ */
442
+ Howl.prototype.pannerAttr = function() {
443
+ var self = this;
444
+ var args = arguments;
445
+ var o, id, sound;
446
+
447
+ // Stop right here if not using Web Audio.
448
+ if (!self._webAudio) {
449
+ return self;
450
+ }
451
+
452
+ // Determine the values based on arguments.
453
+ if (args.length === 0) {
454
+ // Return the group's panner attribute values.
455
+ return self._pannerAttr;
456
+ } else if (args.length === 1) {
457
+ if (typeof args[0] === 'object') {
458
+ o = args[0];
459
+
460
+ // Set the grou's panner attribute values.
461
+ if (typeof id === 'undefined') {
462
+ if (!o.pannerAttr) {
463
+ o.pannerAttr = {
464
+ coneInnerAngle: o.coneInnerAngle,
465
+ coneOuterAngle: o.coneOuterAngle,
466
+ coneOuterGain: o.coneOuterGain,
467
+ distanceModel: o.distanceModel,
468
+ maxDistance: o.maxDistance,
469
+ refDistance: o.refDistance,
470
+ rolloffFactor: o.rolloffFactor,
471
+ panningModel: o.panningModel
472
+ };
473
+ }
474
+
475
+ self._pannerAttr = {
476
+ coneInnerAngle: typeof o.pannerAttr.coneInnerAngle !== 'undefined' ? o.pannerAttr.coneInnerAngle : self._coneInnerAngle,
477
+ coneOuterAngle: typeof o.pannerAttr.coneOuterAngle !== 'undefined' ? o.pannerAttr.coneOuterAngle : self._coneOuterAngle,
478
+ coneOuterGain: typeof o.pannerAttr.coneOuterGain !== 'undefined' ? o.pannerAttr.coneOuterGain : self._coneOuterGain,
479
+ distanceModel: typeof o.pannerAttr.distanceModel !== 'undefined' ? o.pannerAttr.distanceModel : self._distanceModel,
480
+ maxDistance: typeof o.pannerAttr.maxDistance !== 'undefined' ? o.pannerAttr.maxDistance : self._maxDistance,
481
+ refDistance: typeof o.pannerAttr.refDistance !== 'undefined' ? o.pannerAttr.refDistance : self._refDistance,
482
+ rolloffFactor: typeof o.pannerAttr.rolloffFactor !== 'undefined' ? o.pannerAttr.rolloffFactor : self._rolloffFactor,
483
+ panningModel: typeof o.pannerAttr.panningModel !== 'undefined' ? o.pannerAttr.panningModel : self._panningModel
484
+ };
485
+ }
486
+ } else {
487
+ // Return this sound's panner attribute values.
488
+ sound = self._soundById(parseInt(args[0], 10));
489
+ return sound ? sound._pannerAttr : self._pannerAttr;
490
+ }
491
+ } else if (args.length === 2) {
492
+ o = args[0];
493
+ id = parseInt(args[1], 10);
494
+ }
495
+
496
+ // Update the values of the specified sounds.
497
+ var ids = self._getSoundIds(id);
498
+ for (var i=0; i<ids.length; i++) {
499
+ sound = self._soundById(ids[i]);
500
+
501
+ if (sound) {
502
+ // Merge the new values into the sound.
503
+ var pa = sound._pannerAttr;
504
+ pa = {
505
+ coneInnerAngle: typeof o.coneInnerAngle !== 'undefined' ? o.coneInnerAngle : pa.coneInnerAngle,
506
+ coneOuterAngle: typeof o.coneOuterAngle !== 'undefined' ? o.coneOuterAngle : pa.coneOuterAngle,
507
+ coneOuterGain: typeof o.coneOuterGain !== 'undefined' ? o.coneOuterGain : pa.coneOuterGain,
508
+ distanceModel: typeof o.distanceModel !== 'undefined' ? o.distanceModel : pa.distanceModel,
509
+ maxDistance: typeof o.maxDistance !== 'undefined' ? o.maxDistance : pa.maxDistance,
510
+ refDistance: typeof o.refDistance !== 'undefined' ? o.refDistance : pa.refDistance,
511
+ rolloffFactor: typeof o.rolloffFactor !== 'undefined' ? o.rolloffFactor : pa.rolloffFactor,
512
+ panningModel: typeof o.panningModel !== 'undefined' ? o.panningModel : pa.panningModel
513
+ };
514
+
515
+ // Create a new panner node if one doesn't already exist.
516
+ var panner = sound._panner;
517
+ if (!panner) {
518
+ // Make sure we have a position to setup the node with.
519
+ if (!sound._pos) {
520
+ sound._pos = self._pos || [0, 0, -0.5];
521
+ }
522
+
523
+ // Create a new panner node.
524
+ setupPanner(sound, 'spatial');
525
+ panner = sound._panner
526
+ }
527
+
528
+ // Update the panner values or create a new panner if none exists.
529
+ panner.coneInnerAngle = pa.coneInnerAngle;
530
+ panner.coneOuterAngle = pa.coneOuterAngle;
531
+ panner.coneOuterGain = pa.coneOuterGain;
532
+ panner.distanceModel = pa.distanceModel;
533
+ panner.maxDistance = pa.maxDistance;
534
+ panner.refDistance = pa.refDistance;
535
+ panner.rolloffFactor = pa.rolloffFactor;
536
+ panner.panningModel = pa.panningModel;
537
+ }
538
+ }
539
+
540
+ return self;
541
+ };
542
+
543
+ /** Single Sound Methods **/
544
+ /***************************************************************************/
545
+
546
+ /**
547
+ * Add new properties to the core Sound init.
548
+ * @param {Function} _super Core Sound init method.
549
+ * @return {Sound}
550
+ */
551
+ Sound.prototype.init = (function(_super) {
552
+ return function() {
553
+ var self = this;
554
+ var parent = self._parent;
555
+
556
+ // Setup user-defined default properties.
557
+ self._orientation = parent._orientation;
558
+ self._stereo = parent._stereo;
559
+ self._pos = parent._pos;
560
+ self._pannerAttr = parent._pannerAttr;
561
+
562
+ // Complete initilization with howler.js core Sound's init function.
563
+ _super.call(this);
564
+
565
+ // If a stereo or position was specified, set it up.
566
+ if (self._stereo) {
567
+ parent.stereo(self._stereo);
568
+ } else if (self._pos) {
569
+ parent.pos(self._pos[0], self._pos[1], self._pos[2], self._id);
570
+ }
571
+ };
572
+ })(Sound.prototype.init);
573
+
574
+ /**
575
+ * Override the Sound.reset method to clean up properties from the spatial plugin.
576
+ * @param {Function} _super Sound reset method.
577
+ * @return {Sound}
578
+ */
579
+ Sound.prototype.reset = (function(_super) {
580
+ return function() {
581
+ var self = this;
582
+ var parent = self._parent;
583
+
584
+ // Reset all spatial plugin properties on this sound.
585
+ self._orientation = parent._orientation;
586
+ self._stereo = parent._stereo;
587
+ self._pos = parent._pos;
588
+ self._pannerAttr = parent._pannerAttr;
589
+
590
+ // If a stereo or position was specified, set it up.
591
+ if (self._stereo) {
592
+ parent.stereo(self._stereo);
593
+ } else if (self._pos) {
594
+ parent.pos(self._pos[0], self._pos[1], self._pos[2], self._id);
595
+ } else if (self._panner) {
596
+ // Disconnect the panner.
597
+ self._panner.disconnect(0);
598
+ self._panner = undefined;
599
+ parent._refreshBuffer(self);
600
+ }
601
+
602
+ // Complete resetting of the sound.
603
+ return _super.call(this);
604
+ };
605
+ })(Sound.prototype.reset);
606
+
607
+ /** Helper Methods **/
608
+ /***************************************************************************/
609
+
610
+ /**
611
+ * Create a new panner node and save it on the sound.
612
+ * @param {Sound} sound Specific sound to setup panning on.
613
+ * @param {String} type Type of panner to create: 'stereo' or 'spatial'.
614
+ */
615
+ var setupPanner = function(sound, type) {
616
+ type = type || 'spatial';
617
+
618
+ // Create the new panner node.
619
+ if (type === 'spatial') {
620
+ sound._panner = Howler.ctx.createPanner();
621
+ sound._panner.coneInnerAngle = sound._pannerAttr.coneInnerAngle;
622
+ sound._panner.coneOuterAngle = sound._pannerAttr.coneOuterAngle;
623
+ sound._panner.coneOuterGain = sound._pannerAttr.coneOuterGain;
624
+ sound._panner.distanceModel = sound._pannerAttr.distanceModel;
625
+ sound._panner.maxDistance = sound._pannerAttr.maxDistance;
626
+ sound._panner.refDistance = sound._pannerAttr.refDistance;
627
+ sound._panner.rolloffFactor = sound._pannerAttr.rolloffFactor;
628
+ sound._panner.panningModel = sound._pannerAttr.panningModel;
629
+
630
+ if (typeof sound._panner.positionX !== 'undefined') {
631
+ sound._panner.positionX.setValueAtTime(sound._pos[0], Howler.ctx.currentTime);
632
+ sound._panner.positionY.setValueAtTime(sound._pos[1], Howler.ctx.currentTime);
633
+ sound._panner.positionZ.setValueAtTime(sound._pos[2], Howler.ctx.currentTime);
634
+ } else {
635
+ sound._panner.setPosition(sound._pos[0], sound._pos[1], sound._pos[2]);
636
+ }
637
+
638
+ if (typeof sound._panner.orientationX !== 'undefined') {
639
+ sound._panner.orientationX.setValueAtTime(sound._orientation[0], Howler.ctx.currentTime);
640
+ sound._panner.orientationY.setValueAtTime(sound._orientation[1], Howler.ctx.currentTime);
641
+ sound._panner.orientationZ.setValueAtTime(sound._orientation[2], Howler.ctx.currentTime);
642
+ } else {
643
+ sound._panner.setOrientation(sound._orientation[0], sound._orientation[1], sound._orientation[2]);
644
+ }
645
+ } else {
646
+ sound._panner = Howler.ctx.createStereoPanner();
647
+ sound._panner.pan.setValueAtTime(sound._stereo, Howler.ctx.currentTime);
648
+ }
649
+
650
+ sound._panner.connect(sound._node);
651
+
652
+ // Update the connections.
653
+ if (!sound._paused) {
654
+ sound._parent.pause(sound._id, true).play(sound._id, true);
655
+ }
656
+ };
657
+ };