Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bower.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "SoundJS",
"version": "1.0.0",
"version": "1.1.1",
"homepage": "https://github.com/CreateJS/SoundJS",
"authors": [
"lannymcnie",
Expand Down
2 changes: 1 addition & 1 deletion build/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "SoundJS",
"version": "1.0.0",
"version": "1.1.1",
"description": "SoundJS Docs",
"url": "http://www.createjs.com/soundjs",
"logo": "assets/docs-icon-SoundJS.png",
Expand Down
754 changes: 377 additions & 377 deletions lib/soundjs-NEXT.js

Large diffs are not rendered by default.

877 changes: 496 additions & 381 deletions lib/soundjs.js

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions lib/soundjs.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "soundjs",
"npmName": "soundjs",
"version": "1.0.2",
"version": "1.1.1",
"description": "A JavaScript library that provides a simple API, and powerful features to make working with audio a breeze. Easily ties in audio file loading to PreloadJS.",
"main": "lib/soundjs.js",
"jsdelivr": "lib/soundjs.min.js",
Expand Down
21 changes: 21 additions & 0 deletions src/soundjs/AbstractSoundInstance.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,25 @@ this.createjs = this.createjs || {};
* @since 0.4.0
*/
this.delayTimeoutId = null;

/**
* Web Audio only: the AudioContext time (seconds) the next play should start at. Consumed by the
* play; null or a time in the past means "now". See {{#crossLink "PlayPropsConfig/startAt:property"}}{{/crossLink}}.
* @property startAt
* @type {Number}
* @default null
* @since 1.1.0
*/
this.startAt = null;

/**
* Web Audio only: the AudioContext time (seconds) the current play started, or will start, at.
* @property scheduledAt
* @type {Number}
* @default null
* @since 1.1.0
*/
this.scheduledAt = null;
// TODO consider moving delay into AbstractSoundInstance so it can be handled by plugins


Expand Down Expand Up @@ -399,6 +418,7 @@ this.createjs = this.createjs || {};
this._setStartTime(playProps.startTime);
this._setDuration(playProps.duration);
}
if (playProps.startAt != null) { this.startAt = playProps.startAt; }
return this;
};

Expand Down Expand Up @@ -705,6 +725,7 @@ this.createjs = this.createjs || {};
this._setStartTime(playProps.startTime);
this._setDuration(playProps.duration);
}
if (playProps.startAt != null) { this.startAt = playProps.startAt; }

if (this._playbackResource != null && this._position < this._duration) {
this._paused = false;
Expand Down
12 changes: 12 additions & 0 deletions src/soundjs/data/PlayPropsConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ this.createjs = this.createjs || {};
* <li>pan - The left-right pan of the sound (if supported), between -1 (left) and 1 (right).</li>
* <li>startTime - To create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds.</li>
* <li>duration - To create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds.</li>
* <li>startAt - Web Audio only: the AudioContext time in seconds to start playback at (null or past = now).</li>
* </ul>
*
* <h4>Example</h4>
Expand Down Expand Up @@ -138,6 +139,17 @@ this.createjs = this.createjs || {};
* @default null
*/
this.duration = null;

/**
* Web Audio only: the AudioContext time (in seconds) at which playback should start. A value in the
* past, or null, starts playback immediately. Starting in the future is sample-accurate, which a
* "now" start is not on devices with a coarse render quantum.
* @property startAt
* @type {number}
* @default null
* @since 1.1.0
*/
this.startAt = null;
};
var p = PlayPropsConfig.prototype = {};
var s = PlayPropsConfig;
Expand Down
90 changes: 86 additions & 4 deletions src/soundjs/webaudio/WebAudioSoundInstance.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,16 @@ this.createjs = this.createjs || {};
*/
this.sourceNode = null;

/**
* Whether the current play is an infinite loop on a single, natively looping source node
* (see {{#crossLink "WebAudioSoundInstance/nativeLoops:property"}}{{/crossLink}}).
* @property nativeLoop
* @type {Boolean}
* @default false
* @since 1.1.0
*/
this.nativeLoop = false;


// private properties
/**
Expand Down Expand Up @@ -160,6 +170,21 @@ this.createjs = this.createjs || {};
*/
s.destinationNode = null;

/**
* Play infinite loops (loop = -1) on ONE source node with <code>loop = true</code> instead of a chain of
* one node per repetition. The chain is refilled from a timer armed relative to when it last ran, so
* main-thread stalls accumulate and once they add up to a loop length the next repetition is created
* after it should have started: an audible gap. A native loop involves no JS while it plays. It
* dispatches no "loop" event, and its {{#crossLink "AbstractSoundInstance/position:property"}}{{/crossLink}}
* wraps to the repetition in progress. Finite loop counts always use the chain.
* @property nativeLoops
* @type {Boolean}
* @default true
* @static
* @since 1.1.0
*/
s.nativeLoops = true;

/**
* Value to set panning model to equal power for WebAudioSoundInstance. Can be "equalpower" or 0 depending on browser implementation.
* @property _panningModel
Expand Down Expand Up @@ -193,6 +218,15 @@ this.createjs = this.createjs || {};
};

p._removeLooping = function(value) {
if (this.nativeLoop && this.sourceNode) {
// Let the repetition in progress finish, then complete.
this.sourceNode.loop = false;
var remainingMs = this._duration - this._calculateCurrentPosition();
clearTimeout(this._soundCompleteTimeout);
this._soundCompleteTimeout = setTimeout(this._endedHandler, Math.max(0, remainingMs));
this.nativeLoop = false;
return;
}
this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
};

Expand All @@ -217,6 +251,7 @@ this.createjs = this.createjs || {};
clearTimeout(this._soundCompleteTimeout);

this._playbackStartTime = 0; // This is used by _getPosition
this.nativeLoop = false;
};

/**
Expand Down Expand Up @@ -244,18 +279,61 @@ this.createjs = this.createjs || {};
p._handleSoundReady = function (event) {
this.gainNode.connect(s.destinationNode); // this line can cause a memory leak. Nodes need to be disconnected from the audioDestination or any sequence that leads to it.

// A future startAt is honoured sample-accurately; null or past = now.
var now = s.context.currentTime;
var startAt = this.startAt;
this.startAt = null;
var at = (typeof startAt === "number" && startAt > now) ? startAt : now;
this.scheduledAt = at;
this.nativeLoop = false;

var dur = this._duration * 0.001,
pos = Math.min(Math.max(0, this._position) * 0.001, dur);
this.sourceNode = this._createAndPlayAudioNode((s.context.currentTime - dur), pos);

if (this._loop < 0 && s.nativeLoops) {
this._startNativeLoop(at, pos, dur);
return;
}

this.sourceNode = this._createAndPlayAudioNode((at - dur), pos);
this._playbackStartTime = this.sourceNode.startTime - pos;

this._soundCompleteTimeout = setTimeout(this._endedHandler, (dur - pos) * 1000);
this._soundCompleteTimeout = setTimeout(this._endedHandler, (dur - pos + (at - now)) * 1000);

if(this._loop != 0) {
this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0);
}
};

/**
* Start an infinite loop on one natively looping source node (see
* {{#crossLink "WebAudioSoundInstance/nativeLoops:property"}}{{/crossLink}}).
* @method _startNativeLoop
* @param {Number} at The context time to start at, in seconds.
* @param {Number} pos The position in the sound to start at, in seconds.
* @param {Number} dur The duration of the sound, in seconds.
* @protected
* @since 1.1.0
*/
p._startNativeLoop = function (at, pos, dur) {
if (pos >= dur) { pos = 0; }
var spriteStart = this._startTime * 0.001;

var audioNode = s.context.createBufferSource();
audioNode.buffer = this.playbackResource;
audioNode.connect(this.panNode);
audioNode.loop = true;
audioNode.loopStart = spriteStart;
audioNode.loopEnd = spriteStart + dur;
audioNode.startTime = at;
audioNode.start(at, spriteStart + pos);

this.sourceNode = audioNode;
this._sourceNodeNext = null;
this._playbackStartTime = at - pos;
this.nativeLoop = true;
};

/**
* Creates an audio node using the current src and context, connects it to the gain node, and starts playback.
* @method _createAndPlayAudioNode
Expand All @@ -276,7 +354,7 @@ this.createjs = this.createjs || {};
};

p._pause = function () {
this._position = (s.context.currentTime - this._playbackStartTime) * 1000; // * 1000 to give milliseconds, lets us restart at same point
this._position = this._calculateCurrentPosition(); // lets us restart at same point (wrapped for a native loop)
this.sourceNode = this._cleanUpAudioNode(this.sourceNode);
this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);

Expand All @@ -303,7 +381,11 @@ this.createjs = this.createjs || {};
};

p._calculateCurrentPosition = function () {
return ((s.context.currentTime - this._playbackStartTime) * 1000); // pos in seconds * 1000 to give milliseconds
var ms = (s.context.currentTime - this._playbackStartTime) * 1000; // pos in seconds * 1000 to give milliseconds
if (this.nativeLoop && this._duration > 0 && ms >= this._duration) {
ms = ms % this._duration; // the repetition in progress
}
return ms;
};

p._updatePosition = function () {
Expand Down