X-Git-Url: http://id.pley.net/sound.git/blobdiff_plain/ecabc9fd03e1fbecca682ab281cf19f996c381ee..2d410f3ec8d9a7889cc47086d3e6c30a29fb6d56:/sound.js diff --git a/sound.js b/sound.js index e195ca6..a00d422 100644 --- a/sound.js +++ b/sound.js @@ -36,7 +36,6 @@ function Sound(src) { this._playbackRate = 1; this._played = {}; this._seekable = {}; - this._ended = false; this._autoplay = true; this._loop = false; this._volume = 1; @@ -185,7 +184,7 @@ Sound.prototype = { this.setNetworkState(this.NETWORK.IDLE); this.dispatchEventAsync(new CustomEvent('suspend')); - this.setReadyState(this.READY.FUTURE_DATA); + this.setReadyState(this.READY.METADATA); try { Sound.audioContext.decodeAudioData( @@ -210,7 +209,7 @@ Sound.prototype = { this.setCurrentTime(0); this.dispatchEventAsync(new CustomEvent('durationchange')); - this.setReadyState(this.READY.METADATA); + this.setReadyState(this.READY.ENOUGH_DATA); if (this.autoplaying && this._paused && this._autoplay) this.play(); @@ -239,12 +238,15 @@ Sound.prototype = { if (this.node) return; - if (this._ended && this._playbackRate > 0) - this.setCurrentTime(0); + if (this.endedPlayback()) { + if (this._playbackRate > 0) + this.setCurrentTime(0); + else + this.setCurrentTime(this.duration) + } - if (this._paused || this._ended) { + if (this._paused || this.endedPlayback()) { this._paused = false; - this._ended = false; this.dispatchEventAsync(new CustomEvent('play')); if (this._readyState < this.READY.FUTURE_DATA) @@ -269,8 +271,9 @@ Sound.prototype = { this.node.connect(this.gainNode); this.node.buffer = this.buffer; this.node.playbackRate.value = this._playbackRate; - this.node.start(0, this.nextStartTime); this.node.onended = this.onended.bind(this); + var remainingDuration = this._playbackRate < 0 ? this.nextStartTime : this.buffer.duration - this.nextStartTime; + this.node.start(0, this.nextStartTime, remainingDuration); this.timeUpdateTimer = setInterval(this.sendTimeUpdate.bind(this), 250); }, @@ -294,7 +297,7 @@ Sound.prototype = { if (!this.buffer || !this.node) return; - this.nextStartTime = Sound.audioContext.currentTime - this.startTime; + this.nextStartTime = this._playbackRate * (Sound.audioContext.currentTime - this.startTime); this.stopInternal(); }, @@ -313,18 +316,32 @@ Sound.prototype = { onended: function() { if (this._loop) { + this.nextStartTime = this._playbackRate < 0 ? this.duration : 0; this.stopInternal(); - this.setCurrentTime(0); this.playInternal(); return; } - this._ended = true; - this.nextStartTime = 0; + this.nextStartTime = this._playbackRate < 0 ? 0 : this.duration; this.stopInternal(); this.dispatchEventAsync(new CustomEvent('ended')); }, + endedPlayback: function() { + if (this._readyState < this.READY.METADATA) + return false; + + if (this.currentTime >= this.duration && this._playbackRate >= 0 && !this._loop) + return true; + + if (this.currentTime <= 0 && this._playbackRate <= 0) + return true; + }, + + getEnded: function() { + return this.endedPlayback() && this._playbackRate >= 0; + }, + addEventListener: function(eventName, handler) { if (!this.eventListeners[eventName]) this.eventListeners[eventName] = []; @@ -405,10 +422,10 @@ Sound.prototype = { } if (oldState >= this.READY.FUTURE_DATA && newState <= this.READY.CURRENT_DATA) { - if (this.autoplaying && this._paused && this._autoplay && !this._ended && !this._error) { + if (this.autoplaying && this._paused && this._autoplay && !this.endedPlayback() && !this._error) { this.dispatchEventAsync('timeupdate'); this.dispatchEventAsync('waiting'); - this.nextStartTime = Sound.audioContext.currentTime - this.startTime; + this.nextStartTime = this._playbackRate * (Sound.audioContext.currentTime - this.startTime); this.stopInternal(); } } @@ -462,11 +479,11 @@ Sound.prototype = { getCurrentTime: function() { if (!this.node) return this.nextStartTime; - return this.nextStartTime + Sound.audioContext.currentTime - this.startTime; + return this.nextStartTime + this._playbackRate * (Sound.audioContext.currentTime - this.startTime); }, setCurrentTime: function(time) { - this.nextStartTime = time; + this.nextStartTime = parseFloat(time); this.dispatchEventAsync(new CustomEvent('timeupdate')); if (!this.node) return; @@ -491,14 +508,32 @@ Sound.prototype = { }, setPlaybackRate: function(rate) { - this._playbackRate = rate; + var oldPlaybackRate = this._playbackRate; + this._playbackRate = parseFloat(rate); + this.dispatchEventAsync(new CustomEvent('ratechange')); - if (this.buffer) { - this.stopInternal(); - this.playInternal(); + if (this.node) { + var currentTime = Sound.audioContext.currentTime + this.nextStartTime += oldPlaybackRate * (currentTime - this.startTime); + this.startTime = currentTime; + this.node.playbackRate.value = this._playbackRate; + + if ((oldPlaybackRate <= 0) != (this._playbackRate <= 0)) { + this.stopInternal(); + this.playInternal(); + } } }, + getDefaultPlaybackRate: function() { + return this._defaultPlaybackRate; + }, + + setDefaultPlaybackRate: function(rate) { + this._defaultPlaybackRate = parseFloat(rate); + this.dispatchEventAsync(new CustomEvent('ratechange')); + }, + getVolume: function() { return this._volume; }, @@ -507,7 +542,7 @@ Sound.prototype = { if (this._volume === volume) return; - this._volume = volume; + this._volume = parseFloat(volume); this.dispatchEventAsync(new CustomEvent('volumechange')); if (this.gainNode)