浏览代码

Updating Web Audio stack

davrous 10 年之前
父节点
当前提交
52ac80d340

+ 37 - 29
Babylon/Audio/babylon.analyser.js

@@ -5,8 +5,8 @@ var BABYLON;
             this.SMOOTHING = 0.75;
             this.FFT_SIZE = 512;
             this.BARGRAPHAMPLITUDE = 256;
-            this._debugCanvasWidth = 320;
-            this._debugCanvasHeight = 200;
+            this.DEBUGCANVASPOS = { x: 20, y: 20 };
+            this.DEBUGCANVASSIZE = { width: 320, height: 200 };
             this._scene = scene;
             this._audioEngine = scene.getEngine().getAudioEngine();
             if (this._audioEngine.canUseWebAudio) {
@@ -19,40 +19,47 @@ var BABYLON;
             }
         }
         Analyser.prototype.getFrequencyBinCount = function () {
-            return this._webAudioAnalyser.frequencyBinCount;
+            if (this._audioEngine.canUseWebAudio) {
+                return this._webAudioAnalyser.frequencyBinCount;
+            }
+            else {
+                return 0;
+            }
         };
-
         Analyser.prototype.getByteFrequencyData = function () {
-            this._webAudioAnalyser.smoothingTimeConstant = this.SMOOTHING;
-            this._webAudioAnalyser.fftSize = this.FFT_SIZE;
-            this._webAudioAnalyser.getByteFrequencyData(this._byteFreqs);
+            if (this._audioEngine.canUseWebAudio) {
+                this._webAudioAnalyser.smoothingTimeConstant = this.SMOOTHING;
+                this._webAudioAnalyser.fftSize = this.FFT_SIZE;
+                this._webAudioAnalyser.getByteFrequencyData(this._byteFreqs);
+            }
             return this._byteFreqs;
         };
-
         Analyser.prototype.getByteTimeDomainData = function () {
-            this._webAudioAnalyser.smoothingTimeConstant = this.SMOOTHING;
-            this._webAudioAnalyser.fftSize = this.FFT_SIZE;
-            this._webAudioAnalyser.getByteTimeDomainData(this._byteTime);
+            if (this._audioEngine.canUseWebAudio) {
+                this._webAudioAnalyser.smoothingTimeConstant = this.SMOOTHING;
+                this._webAudioAnalyser.fftSize = this.FFT_SIZE;
+                this._webAudioAnalyser.getByteTimeDomainData(this._byteTime);
+            }
             return this._byteTime;
         };
-
         Analyser.prototype.getFloatFrequencyData = function () {
-            this._webAudioAnalyser.smoothingTimeConstant = this.SMOOTHING;
-            this._webAudioAnalyser.fftSize = this.FFT_SIZE;
-            this._webAudioAnalyser.getFloatFrequencyData(this._floatFreqs);
+            if (this._audioEngine.canUseWebAudio) {
+                this._webAudioAnalyser.smoothingTimeConstant = this.SMOOTHING;
+                this._webAudioAnalyser.fftSize = this.FFT_SIZE;
+                this._webAudioAnalyser.getFloatFrequencyData(this._floatFreqs);
+            }
             return this._floatFreqs;
         };
-
         Analyser.prototype.drawDebugCanvas = function () {
             var _this = this;
             if (this._audioEngine.canUseWebAudio) {
                 if (!this._debugCanvas) {
                     this._debugCanvas = document.createElement("canvas");
-                    this._debugCanvas.width = this._debugCanvasWidth;
-                    this._debugCanvas.height = this._debugCanvasHeight;
+                    this._debugCanvas.width = this.DEBUGCANVASSIZE.width;
+                    this._debugCanvas.height = this.DEBUGCANVASSIZE.height;
                     this._debugCanvas.style.position = "absolute";
-                    this._debugCanvas.style.top = "30px";
-                    this._debugCanvas.style.left = "10px";
+                    this._debugCanvas.style.top = this.DEBUGCANVASPOS.y + "px";
+                    this._debugCanvas.style.left = this.DEBUGCANVASPOS.x + "px";
                     this._debugCanvasContext = this._debugCanvas.getContext("2d");
                     document.body.appendChild(this._debugCanvas);
                     this._registerFunc = function () {
@@ -62,16 +69,14 @@ var BABYLON;
                 }
                 if (this._registerFunc) {
                     var workingArray = this.getByteFrequencyData();
-
                     this._debugCanvasContext.fillStyle = 'rgb(0, 0, 0)';
-                    this._debugCanvasContext.fillRect(0, 0, this._debugCanvasWidth, this._debugCanvasHeight);
-
+                    this._debugCanvasContext.fillRect(0, 0, this.DEBUGCANVASSIZE.width, this.DEBUGCANVASSIZE.height);
                     for (var i = 0; i < this.getFrequencyBinCount(); i++) {
                         var value = workingArray[i];
                         var percent = value / this.BARGRAPHAMPLITUDE;
-                        var height = this._debugCanvasHeight * percent;
-                        var offset = this._debugCanvasHeight - height - 1;
-                        var barWidth = this._debugCanvasWidth / this.getFrequencyBinCount();
+                        var height = this.DEBUGCANVASSIZE.height * percent;
+                        var offset = this.DEBUGCANVASSIZE.height - height - 1;
+                        var barWidth = this.DEBUGCANVASSIZE.width / this.getFrequencyBinCount();
                         var hue = i / this.getFrequencyBinCount() * 360;
                         this._debugCanvasContext.fillStyle = 'hsl(' + hue + ', 100%, 50%)';
                         this._debugCanvasContext.fillRect(i * barWidth, offset, barWidth, height);
@@ -79,7 +84,6 @@ var BABYLON;
                 }
             }
         };
-
         Analyser.prototype.stopDebugCanvas = function () {
             if (this._debugCanvas) {
                 this._scene.unregisterBeforeRender(this._registerFunc);
@@ -89,15 +93,19 @@ var BABYLON;
                 this._debugCanvasContext = null;
             }
         };
-
         Analyser.prototype.connectAudioNodes = function (inputAudioNode, outputAudioNode) {
             if (this._audioEngine.canUseWebAudio) {
                 inputAudioNode.connect(this._webAudioAnalyser);
                 this._webAudioAnalyser.connect(outputAudioNode);
             }
         };
+        Analyser.prototype.dispose = function () {
+            if (this._audioEngine.canUseWebAudio) {
+                this._webAudioAnalyser.disconnect();
+            }
+        };
         return Analyser;
     })();
     BABYLON.Analyser = Analyser;
 })(BABYLON || (BABYLON = {}));
-//# sourceMappingURL=babylon.analyser.js.map
+//# sourceMappingURL=babylon.analyser.js.map

+ 6 - 0
Babylon/Audio/babylon.analyser.ts

@@ -118,5 +118,11 @@ module BABYLON {
                 this._webAudioAnalyser.connect(outputAudioNode);
             }
         }
+
+        public dispose() {
+            if (this._audioEngine.canUseWebAudio) {
+                this._webAudioAnalyser.disconnect();
+            }
+        }
     }
 }

+ 31 - 12
Babylon/Audio/babylon.audioengine.js

@@ -1,51 +1,70 @@
-var BABYLON;
+var BABYLON;
 (function (BABYLON) {
     var AudioEngine = (function () {
         function AudioEngine() {
             this.audioContext = null;
             this.canUseWebAudio = false;
-            try  {
+            try {
                 if (typeof AudioContext !== 'undefined') {
                     this.audioContext = new AudioContext();
                     this.canUseWebAudio = true;
-                } else if (typeof webkitAudioContext !== 'undefined') {
+                }
+                else if (typeof webkitAudioContext !== 'undefined') {
                     this.audioContext = new webkitAudioContext();
                     this.canUseWebAudio = true;
                 }
-            } catch (e) {
+                else {
+                    BABYLON.Tools.Error("Web Audio is not supported by your browser.");
+                }
+            }
+            catch (e) {
                 this.canUseWebAudio = false;
-                BABYLON.Tools.Error("Your browser doesn't support Web Audio.");
+                BABYLON.Tools.Error("Web Audio: " + e.message);
             }
-
-            // create a global volume gain node
+            // create a global volume gain node 
             if (this.canUseWebAudio) {
                 this.masterGain = this.audioContext.createGain();
                 this.masterGain.gain.value = 1;
                 this.masterGain.connect(this.audioContext.destination);
             }
         }
+        AudioEngine.prototype.dispose = function () {
+            if (this.canUseWebAudio) {
+                if (this._connectedAnalyser) {
+                    this._connectedAnalyser.stopDebugCanvas();
+                    this._connectedAnalyser.dispose();
+                    this.masterGain.disconnect();
+                    this.masterGain.connect(this.audioContext.destination);
+                    this._connectedAnalyser = null;
+                }
+                this.masterGain.gain.value = 1;
+            }
+        };
         AudioEngine.prototype.getGlobalVolume = function () {
             if (this.canUseWebAudio) {
                 return this.masterGain.gain.value;
-            } else {
+            }
+            else {
                 return -1;
             }
         };
-
         AudioEngine.prototype.setGlobalVolume = function (newVolume) {
             if (this.canUseWebAudio) {
                 this.masterGain.gain.value = newVolume;
             }
         };
-
         AudioEngine.prototype.connectToAnalyser = function (analyser) {
+            if (this._connectedAnalyser) {
+                this._connectedAnalyser.stopDebugCanvas();
+            }
+            this._connectedAnalyser = analyser;
             if (this.canUseWebAudio) {
                 this.masterGain.disconnect();
-                analyser.connectAudioNodes(this.masterGain, this.audioContext.destination);
+                this._connectedAnalyser.connectAudioNodes(this.masterGain, this.audioContext.destination);
             }
         };
         return AudioEngine;
     })();
     BABYLON.AudioEngine = AudioEngine;
 })(BABYLON || (BABYLON = {}));
-//# sourceMappingURL=babylon.audioengine.js.map
+//# sourceMappingURL=babylon.audioengine.js.map

+ 10 - 6
Babylon/Audio/babylon.audioengine.ts

@@ -15,10 +15,13 @@
                 } else if (typeof webkitAudioContext !== 'undefined') {
                     this.audioContext = new webkitAudioContext();
                     this.canUseWebAudio = true;
-                } 
+                }
+                else {
+                    BABYLON.Tools.Error("Web Audio is not supported by your browser.");
+                }
             } catch (e) {
                 this.canUseWebAudio = false;
-                BABYLON.Tools.Error("Your browser doesn't support Web Audio.");
+                BABYLON.Tools.Error("Web Audio: " + e.message);
             }
 
             // create a global volume gain node 
@@ -33,11 +36,12 @@
             if (this.canUseWebAudio) {
                 if (this._connectedAnalyser) {
                     this._connectedAnalyser.stopDebugCanvas();
+                    this._connectedAnalyser.dispose();
+                    this.masterGain.disconnect();
+                    this.masterGain.connect(this.audioContext.destination);
+                    this._connectedAnalyser = null;
                 }
-                this.canUseWebAudio = false;
-                this.masterGain.disconnect();
-                this.masterGain = null;
-                this.audioContext = null;
+                this.masterGain.gain.value = 1;
             }
         }
 

+ 85 - 63
Babylon/Audio/babylon.sound.js

@@ -1,4 +1,4 @@
-var BABYLON;
+var BABYLON;
 (function (BABYLON) {
     var Sound = (function () {
         /**
@@ -19,8 +19,9 @@
             this.maxDistance = 100;
             this.distanceModel = "linear";
             this.panningModel = "HRTF";
-            this.startTime = 0;
-            this.startOffset = 0;
+            this.playbackRate = 1;
+            this._startTime = 0;
+            this._startOffset = 0;
             this._position = BABYLON.Vector3.Zero();
             this._localDirection = new BABYLON.Vector3(1, 0, 0);
             this._volume = 1;
@@ -33,54 +34,86 @@
             this._coneInnerAngle = 360;
             this._coneOuterAngle = 360;
             this._coneOuterGain = 0;
-            this._name = name;
+            this.name = name;
             this._scene = scene;
             this._audioEngine = this._scene.getEngine().getAudioEngine();
             this._readyToPlayCallback = readyToPlayCallback;
-
             // Default custom attenuation function is a linear attenuation
             this._customAttenuationFunction = function (currentVolume, currentDistance, maxDistance, refDistance, rolloffFactor) {
                 if (currentDistance < maxDistance) {
                     return currentVolume * (1 - currentDistance / maxDistance);
-                } else {
+                }
+                else {
                     return 0;
                 }
             };
             if (options) {
                 this.autoplay = options.autoplay || false;
                 this.loop = options.loop || false;
-                this._volume = options.volume || 1;
+                // if volume === 0, we need another way to check this option
+                if (options.volume !== undefined) {
+                    this._volume = options.volume;
+                }
                 this.spatialSound = options.spatialSound || false;
                 this.maxDistance = options.maxDistance || 100;
-                this.useCustomAttenuation = options.useCustomAttenation || false;
+                this.useCustomAttenuation = options.useCustomAttenuation || false;
                 this.rolloffFactor = options.rolloffFactor || 1;
                 this.refDistance = options.refDistance || 1;
                 this.distanceModel = options.distanceModel || "linear";
                 this.panningModel = options.panningModel || "HRTF";
+                this.playbackRate = options.playbackRate || 1;
             }
-
             if (this._audioEngine.canUseWebAudio) {
                 this._soundGain = this._audioEngine.audioContext.createGain();
                 this._soundGain.gain.value = this._volume;
+                this._inputAudioNode = this._soundGain;
+                this._ouputAudioNode = this._soundGain;
                 if (this.spatialSound) {
                     this._createSpatialParameters();
-                } else {
-                    this._audioNode = this._soundGain;
                 }
                 this._scene.mainSoundTrack.AddSound(this);
                 if (typeof (urlOrArrayBuffer) === "string") {
                     BABYLON.Tools.LoadFile(urlOrArrayBuffer, function (data) {
                         _this._soundLoaded(data);
                     }, null, null, true);
-                } else {
+                }
+                else {
                     if (urlOrArrayBuffer instanceof ArrayBuffer) {
                         this._soundLoaded(urlOrArrayBuffer);
-                    } else {
+                    }
+                    else {
                         BABYLON.Tools.Error("Parameter must be a URL to the sound or an ArrayBuffer of the sound.");
                     }
                 }
             }
         }
+        Sound.prototype.dispose = function () {
+            if (this._audioEngine.canUseWebAudio && this._isReadyToPlay) {
+                if (this._isPlaying) {
+                    this.stop();
+                }
+                this._isReadyToPlay = false;
+                if (this.soundTrackId === -1) {
+                    this._scene.mainSoundTrack.RemoveSound(this);
+                }
+                else {
+                    this._scene.soundTracks[this.soundTrackId].RemoveSound(this);
+                }
+                this._soundGain.disconnect();
+                this._soundSource.disconnect();
+                if (this._soundPanner) {
+                    this._soundPanner.disconnect();
+                    this._soundPanner = null;
+                }
+                this._audioBuffer = null;
+                this._soundGain = null;
+                this._soundSource = null;
+                if (this._connectedMesh) {
+                    this._connectedMesh.unregisterAfterWorldMatrixUpdate(this._registerFunc);
+                    this._connectedMesh = null;
+                }
+            }
+        };
         Sound.prototype._soundLoaded = function (audioData) {
             var _this = this;
             this._isLoaded = true;
@@ -97,49 +130,46 @@
                 BABYLON.Tools.Error("Error while decoding audio data: " + error.err);
             });
         };
-
         Sound.prototype.updateOptions = function (options) {
             if (options) {
                 this.loop = options.loop || this.loop;
                 this.maxDistance = options.maxDistance || this.maxDistance;
-                this.useCustomAttenuation = options.useCustomAttenation || this.useCustomAttenuation;
+                this.useCustomAttenuation = options.useCustomAttenuation || this.useCustomAttenuation;
                 this.rolloffFactor = options.rolloffFactor || this.rolloffFactor;
                 this.refDistance = options.refDistance || this.refDistance;
                 this.distanceModel = options.distanceModel || this.distanceModel;
                 this.panningModel = options.panningModel || this.panningModel;
+                this.playbackRate = options.playbackRate || this.playbackRate;
             }
         };
-
         Sound.prototype._createSpatialParameters = function () {
             if (this._audioEngine.canUseWebAudio) {
                 this._soundPanner = this._audioEngine.audioContext.createPanner();
-
                 if (this.useCustomAttenuation) {
-                    // Tricks to disable in a way embedded Web Audio attenuation
+                    // Tricks to disable in a way embedded Web Audio attenuation 
                     this._soundPanner.distanceModel = "linear";
                     this._soundPanner.maxDistance = Number.MAX_VALUE;
                     this._soundPanner.refDistance = 1;
                     this._soundPanner.rolloffFactor = 1;
                     this._soundPanner.panningModel = "HRTF";
-                } else {
+                }
+                else {
                     this._soundPanner.distanceModel = this.distanceModel;
                     this._soundPanner.maxDistance = this.maxDistance;
                     this._soundPanner.refDistance = this.refDistance;
                     this._soundPanner.rolloffFactor = this.rolloffFactor;
                     this._soundPanner.panningModel = this.panningModel;
                 }
-                this._soundPanner.connect(this._soundGain);
-                this._audioNode = this._soundPanner;
+                this._soundPanner.connect(this._ouputAudioNode);
+                this._inputAudioNode = this._soundPanner;
             }
         };
-
         Sound.prototype.connectToSoundTrackAudioNode = function (soundTrackAudioNode) {
             if (this._audioEngine.canUseWebAudio) {
-                this._audioNode.disconnect();
-                this._audioNode.connect(soundTrackAudioNode);
+                this._ouputAudioNode.disconnect();
+                this._ouputAudioNode.connect(soundTrackAudioNode);
             }
         };
-
         /**
         * Transform this sound into a directional source
         * @param coneInnerAngle Size of the inner cone in degree
@@ -155,81 +185,79 @@
             this._coneOuterAngle = coneOuterAngle;
             this._coneOuterGain = coneOuterGain;
             this._isDirectional = true;
-
             if (this._isPlaying && this.loop) {
                 this.stop();
                 this.play();
             }
         };
-
         Sound.prototype.setPosition = function (newPosition) {
             this._position = newPosition;
-
             if (this._isPlaying && this.spatialSound) {
                 this._soundPanner.setPosition(this._position.x, this._position.y, this._position.z);
             }
         };
-
         Sound.prototype.setLocalDirectionToMesh = function (newLocalDirection) {
             this._localDirection = newLocalDirection;
-
             if (this._connectedMesh && this._isPlaying) {
                 this._updateDirection();
             }
         };
-
         Sound.prototype._updateDirection = function () {
             var mat = this._connectedMesh.getWorldMatrix();
             var direction = BABYLON.Vector3.TransformNormal(this._localDirection, mat);
             direction.normalize();
             this._soundPanner.setOrientation(direction.x, direction.y, direction.z);
         };
-
         Sound.prototype.updateDistanceFromListener = function () {
             if (this._connectedMesh && this.useCustomAttenuation) {
                 var distance = this._connectedMesh.getDistanceToCamera(this._scene.activeCamera);
                 this._soundGain.gain.value = this._customAttenuationFunction(this._volume, distance, this.maxDistance, this.refDistance, this.rolloffFactor);
             }
         };
-
         Sound.prototype.setAttenuationFunction = function (callback) {
             this._customAttenuationFunction = callback;
         };
-
         /**
         * Play the sound
         * @param time (optional) Start the sound after X seconds. Start immediately (0) by default.
         */
         Sound.prototype.play = function (time) {
             if (this._isReadyToPlay) {
-                try  {
+                try {
                     var startTime = time ? this._audioEngine.audioContext.currentTime + time : 0;
-                    this._soundSource = this._audioEngine.audioContext.createBufferSource();
-                    this._soundSource.buffer = this._audioBuffer;
-                    if (this.spatialSound) {
-                        this._soundPanner.setPosition(this._position.x, this._position.y, this._position.z);
-                        if (this._isDirectional) {
-                            this._soundPanner.coneInnerAngle = this._coneInnerAngle;
-                            this._soundPanner.coneOuterAngle = this._coneOuterAngle;
-                            this._soundPanner.coneOuterGain = this._coneOuterGain;
-                            if (this._connectedMesh) {
-                                this._updateDirection();
-                            } else {
-                                this._soundPanner.setOrientation(this._localDirection.x, this._localDirection.y, this._localDirection.z);
+                    if (!this._soundSource) {
+                        if (this.spatialSound) {
+                            this._soundPanner.setPosition(this._position.x, this._position.y, this._position.z);
+                            if (this._isDirectional) {
+                                this._soundPanner.coneInnerAngle = this._coneInnerAngle;
+                                this._soundPanner.coneOuterAngle = this._coneOuterAngle;
+                                this._soundPanner.coneOuterGain = this._coneOuterGain;
+                                if (this._connectedMesh) {
+                                    this._updateDirection();
+                                }
+                                else {
+                                    this._soundPanner.setOrientation(this._localDirection.x, this._localDirection.y, this._localDirection.z);
+                                }
                             }
                         }
                     }
-                    this._soundSource.connect(this._audioNode);
+                    this._soundSource = this._audioEngine.audioContext.createBufferSource();
+                    this._soundSource.buffer = this._audioBuffer;
+                    this._soundSource.connect(this._inputAudioNode);
                     this._soundSource.loop = this.loop;
-                    this.startTime = startTime;
-                    this._soundSource.start(startTime, this.startOffset % this._soundSource.buffer.duration);
+                    this._soundSource.playbackRate.value = this.playbackRate;
+                    this._startTime = startTime;
+                    if (this.onended) {
+                        this._soundSource.onended = this.onended;
+                    }
+                    this._soundSource.start(startTime, this._startOffset % this._soundSource.buffer.duration);
                     this._isPlaying = true;
-                } catch (ex) {
-                    BABYLON.Tools.Error("Error while trying to play audio: " + this._name + ", " + ex.message);
+                }
+                catch (ex) {
+                    BABYLON.Tools.Error("Error while trying to play audio: " + this.name + ", " + ex.message);
                 }
             }
         };
-
         /**
         * Stop the sound
         * @param time (optional) Stop the sound after X seconds. Stop immediately (0) by default.
@@ -241,25 +269,21 @@
                 this._isPlaying = false;
             }
         };
-
         Sound.prototype.pause = function () {
             if (this._isPlaying) {
                 this._soundSource.stop(0);
-                this.startOffset += this._audioEngine.audioContext.currentTime - this.startTime;
+                this._startOffset += this._audioEngine.audioContext.currentTime - this._startTime;
             }
         };
-
         Sound.prototype.setVolume = function (newVolume) {
             this._volume = newVolume;
             if (this._audioEngine.canUseWebAudio) {
                 this._soundGain.gain.value = newVolume;
             }
         };
-
         Sound.prototype.getVolume = function () {
             return this._volume;
         };
-
         Sound.prototype.attachToMesh = function (meshToConnectTo) {
             var _this = this;
             this._connectedMesh = meshToConnectTo;
@@ -271,11 +295,9 @@
                     this.play();
                 }
             }
-            meshToConnectTo.registerAfterWorldMatrixUpdate(function (connectedMesh) {
-                return _this._onRegisterAfterWorldMatrixUpdate(connectedMesh);
-            });
+            this._registerFunc = function (connectedMesh) { return _this._onRegisterAfterWorldMatrixUpdate(connectedMesh); };
+            meshToConnectTo.registerAfterWorldMatrixUpdate(this._registerFunc);
         };
-
         Sound.prototype._onRegisterAfterWorldMatrixUpdate = function (connectedMesh) {
             this.setPosition(connectedMesh.position);
             if (this._isDirectional && this._isPlaying) {
@@ -286,4 +308,4 @@
     })();
     BABYLON.Sound = Sound;
 })(BABYLON || (BABYLON = {}));
-//# sourceMappingURL=babylon.sound.js.map
+//# sourceMappingURL=babylon.sound.js.map

+ 41 - 29
Babylon/Audio/babylon.sound.ts

@@ -11,6 +11,8 @@
         public maxDistance: number = 100;
         public distanceModel: string = "linear";
         public panningModel: string = "HRTF";
+        public playbackRate: number = 1;
+        public onended: () => any;
         private _startTime: number = 0;
         private _startOffset: number = 0;
         private _position: Vector3 = Vector3.Zero();
@@ -26,7 +28,8 @@
         private _soundSource: AudioBufferSourceNode;
         private _soundPanner: PannerNode;
         private _soundGain: GainNode;
-        private _audioNode: AudioNode;
+        private _inputAudioNode: AudioNode;
+        private _ouputAudioNode: AudioNode;
         // Used if you'd like to create a directional sound.
         // If not set, the sound will be omnidirectional
         private _coneInnerAngle: number = 360;
@@ -61,25 +64,28 @@
             if (options) {
                 this.autoplay = options.autoplay || false;
                 this.loop = options.loop || false;
-                this._volume = options.volume || 1;
+                // if volume === 0, we need another way to check this option
+                if (options.volume !== undefined) {
+                    this._volume = options.volume;
+                }
                 this.spatialSound = options.spatialSound || false;
                 this.maxDistance = options.maxDistance || 100;
-                this.useCustomAttenuation = options.useCustomAttenation || false;
+                this.useCustomAttenuation = options.useCustomAttenuation || false;
                 this.rolloffFactor = options.rolloffFactor || 1;
                 this.refDistance = options.refDistance || 1;
                 this.distanceModel = options.distanceModel || "linear";
                 this.panningModel = options.panningModel || "HRTF";
+                this.playbackRate = options.playbackRate || 1;
             }
 
             if (this._audioEngine.canUseWebAudio) {
                 this._soundGain = this._audioEngine.audioContext.createGain();
                 this._soundGain.gain.value = this._volume;
+                this._inputAudioNode = this._soundGain;
+                this._ouputAudioNode = this._soundGain;
                 if (this.spatialSound) {
                     this._createSpatialParameters();
                 }
-                else {
-                    this._audioNode = this._soundGain;
-                }
                 this._scene.mainSoundTrack.AddSound(this);
                 if (typeof (urlOrArrayBuffer) === "string") {
                     Tools.LoadFile(urlOrArrayBuffer, (data) => { this._soundLoaded(data); }, null, null, true);
@@ -109,14 +115,13 @@
                 }
                 this._soundGain.disconnect();
                 this._soundSource.disconnect();
-                this._audioBuffer = null;
-                this._soundGain = null;
-                this._soundSource = null;
                 if (this._soundPanner) {
                     this._soundPanner.disconnect();
                     this._soundPanner = null;
                 }
-                this._audioNode.disconnect();
+                this._audioBuffer = null;
+                this._soundGain = null;
+                this._soundSource = null;
                 if (this._connectedMesh) {
                     this._connectedMesh.unregisterAfterWorldMatrixUpdate(this._registerFunc);
                     this._connectedMesh = null;
@@ -138,11 +143,12 @@
             if (options) {
                 this.loop = options.loop || this.loop;
                 this.maxDistance = options.maxDistance || this.maxDistance;
-                this.useCustomAttenuation = options.useCustomAttenation || this.useCustomAttenuation;
+                this.useCustomAttenuation = options.useCustomAttenuation || this.useCustomAttenuation;
                 this.rolloffFactor = options.rolloffFactor || this.rolloffFactor;
                 this.refDistance = options.refDistance || this.refDistance;
                 this.distanceModel = options.distanceModel || this.distanceModel;
                 this.panningModel = options.panningModel || this.panningModel;
+                this.playbackRate = options.playbackRate || this.playbackRate;
             }
         }
 
@@ -165,15 +171,15 @@
                     this._soundPanner.rolloffFactor = this.rolloffFactor;
                     this._soundPanner.panningModel = this.panningModel;
                 }
-                this._soundPanner.connect(this._soundGain);
-                this._audioNode = this._soundPanner;
+                this._soundPanner.connect(this._ouputAudioNode);
+                this._inputAudioNode = this._soundPanner;
             }
         }
 
         public connectToSoundTrackAudioNode(soundTrackAudioNode: AudioNode) {
             if (this._audioEngine.canUseWebAudio) {
-                this._audioNode.disconnect();
-                this._audioNode.connect(soundTrackAudioNode);
+                this._ouputAudioNode.disconnect();
+                this._ouputAudioNode.connect(soundTrackAudioNode);
             }
         }
 
@@ -241,25 +247,31 @@
             if (this._isReadyToPlay) {
                 try {
                     var startTime = time ? this._audioEngine.audioContext.currentTime + time : 0;
-                    this._soundSource = this._audioEngine.audioContext.createBufferSource();
-                    this._soundSource.buffer = this._audioBuffer;
-                    if (this.spatialSound) {
-                        this._soundPanner.setPosition(this._position.x, this._position.y, this._position.z);
-                        if (this._isDirectional) {
-                            this._soundPanner.coneInnerAngle = this._coneInnerAngle;
-                            this._soundPanner.coneOuterAngle = this._coneOuterAngle;
-                            this._soundPanner.coneOuterGain = this._coneOuterGain;
-                            if (this._connectedMesh) {
-                                this._updateDirection();
-                            }
-                            else {
-                                this._soundPanner.setOrientation(this._localDirection.x, this._localDirection.y, this._localDirection.z);
+                    if (!this._soundSource) {
+                        if (this.spatialSound) {
+                            this._soundPanner.setPosition(this._position.x, this._position.y, this._position.z);
+                            if (this._isDirectional) {
+                                this._soundPanner.coneInnerAngle = this._coneInnerAngle;
+                                this._soundPanner.coneOuterAngle = this._coneOuterAngle;
+                                this._soundPanner.coneOuterGain = this._coneOuterGain;
+                                if (this._connectedMesh) {
+                                    this._updateDirection();
+                                }
+                                else {
+                                    this._soundPanner.setOrientation(this._localDirection.x, this._localDirection.y, this._localDirection.z);
+                                }
                             }
                         }
                     }
-                    this._soundSource.connect(this._audioNode);
+                    this._soundSource = this._audioEngine.audioContext.createBufferSource();
+                    this._soundSource.buffer = this._audioBuffer;
+                    this._soundSource.connect(this._inputAudioNode);
                     this._soundSource.loop = this.loop;
+                    this._soundSource.playbackRate.value = this.playbackRate;
                     this._startTime = startTime;
+                    if (this.onended) {
+                        this._soundSource.onended = this.onended;
+                    }
                     this._soundSource.start(startTime, this._startOffset % this._soundSource.buffer.duration);
                     this._isPlaying = true;
                 }

+ 26 - 9
Babylon/Audio/babylon.soundtrack.js

@@ -1,4 +1,4 @@
-var BABYLON;
+var BABYLON;
 (function (BABYLON) {
     var SoundTrack = (function () {
         function SoundTrack(scene, options) {
@@ -9,11 +9,7 @@
             this.soundCollection = new Array();
             if (this._audioEngine.canUseWebAudio) {
                 this._trackGain = this._audioEngine.audioContext.createGain();
-
-                //this._trackConvolver = this._audioEngine.audioContext.createConvolver();
-                //this._trackConvolver.connect(this._trackGain);
                 this._trackGain.connect(this._audioEngine.masterGain);
-
                 if (options) {
                     if (options.volume) {
                         this._trackGain.gain.value = options.volume;
@@ -28,33 +24,54 @@
                 this.id = this._scene.soundTracks.length - 1;
             }
         }
+        SoundTrack.prototype.dispose = function () {
+            if (this._audioEngine.canUseWebAudio) {
+                if (this._connectedAnalyser) {
+                    this._connectedAnalyser.stopDebugCanvas();
+                }
+                while (this.soundCollection.length) {
+                    this.soundCollection[0].dispose();
+                }
+                this._trackGain.disconnect();
+                this._trackGain = null;
+            }
+        };
         SoundTrack.prototype.AddSound = function (sound) {
             sound.connectToSoundTrackAudioNode(this._trackGain);
             if (sound.soundTrackId) {
                 if (sound.soundTrackId === -1) {
                     this._scene.mainSoundTrack.RemoveSound(sound);
-                } else {
+                }
+                else {
                     this._scene.soundTracks[sound.soundTrackId].RemoveSound(sound);
                 }
             }
             this.soundCollection.push(sound);
             sound.soundTrackId = this.id;
         };
-
         SoundTrack.prototype.RemoveSound = function (sound) {
             var index = this.soundCollection.indexOf(sound);
             if (index !== -1) {
                 this.soundCollection.splice(index, 1);
             }
         };
-
         SoundTrack.prototype.setVolume = function (newVolume) {
             if (this._audioEngine.canUseWebAudio) {
                 this._trackGain.gain.value = newVolume;
             }
         };
+        SoundTrack.prototype.connectToAnalyser = function (analyser) {
+            if (this._connectedAnalyser) {
+                this._connectedAnalyser.stopDebugCanvas();
+            }
+            this._connectedAnalyser = analyser;
+            if (this._audioEngine.canUseWebAudio) {
+                this._trackGain.disconnect();
+                this._connectedAnalyser.connectAudioNodes(this._trackGain, this._audioEngine.masterGain);
+            }
+        };
         return SoundTrack;
     })();
     BABYLON.SoundTrack = SoundTrack;
 })(BABYLON || (BABYLON = {}));
-//# sourceMappingURL=babylon.soundtrack.js.map
+//# sourceMappingURL=babylon.soundtrack.js.map