2014-10-21 18:55:29 +00:00
|
|
|
define(["Tone/core/Tone", "Tone/core/Buffer", "Tone/source/Source"], function(Tone){
|
2014-09-04 04:41:40 +00:00
|
|
|
|
|
|
|
"use strict";
|
2014-06-19 05:40:16 +00:00
|
|
|
|
2014-06-18 19:42:08 +00:00
|
|
|
/**
|
2014-07-20 22:17:24 +00:00
|
|
|
* @class Audio file player with start, loop, stop.
|
2014-06-18 19:42:08 +00:00
|
|
|
*
|
|
|
|
* @constructor
|
|
|
|
* @extends {Tone.Source}
|
2015-02-27 16:19:45 +00:00
|
|
|
* @param {string|AudioBuffer} url Either the AudioBuffer or the url from
|
2015-01-06 02:49:21 +00:00
|
|
|
* which to load the AudioBuffer
|
2015-02-27 16:19:45 +00:00
|
|
|
* @param {function=} onload The function to invoke when the buffer is loaded.
|
2015-06-14 01:54:20 +00:00
|
|
|
* Recommended to use Tone.Buffer.onload instead.
|
2015-02-27 16:19:45 +00:00
|
|
|
* @example
|
2015-06-14 01:54:20 +00:00
|
|
|
* var player = new Tone.Player("./path/to/sample.mp3");
|
2014-06-18 19:42:08 +00:00
|
|
|
*/
|
2014-09-30 04:28:48 +00:00
|
|
|
Tone.Player = function(){
|
2014-08-20 20:51:02 +00:00
|
|
|
|
2015-01-06 02:55:57 +00:00
|
|
|
var options = this.optionsObject(arguments, ["url", "onload"], Tone.Player.defaults);
|
2015-02-02 01:38:06 +00:00
|
|
|
Tone.Source.call(this, options);
|
2014-03-24 17:41:08 +00:00
|
|
|
|
2014-06-19 05:40:16 +00:00
|
|
|
/**
|
|
|
|
* @private
|
|
|
|
* @type {AudioBufferSourceNode}
|
|
|
|
*/
|
|
|
|
this._source = null;
|
2015-04-28 20:17:54 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* If the file should play as soon
|
|
|
|
* as the buffer is loaded.
|
|
|
|
* @type {boolean}
|
|
|
|
*/
|
|
|
|
this.autostart = options.autostart;
|
2014-06-19 05:40:16 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* the buffer
|
|
|
|
* @private
|
2014-12-03 22:20:23 +00:00
|
|
|
* @type {Tone.Buffer}
|
2014-06-19 05:40:16 +00:00
|
|
|
*/
|
2015-03-26 14:51:44 +00:00
|
|
|
this._buffer = new Tone.Buffer({
|
|
|
|
"url" : options.url,
|
2015-04-28 20:17:54 +00:00
|
|
|
"onload" : this._onload.bind(this, options.onload),
|
2015-03-26 14:51:44 +00:00
|
|
|
"reverse" : options.reverse
|
|
|
|
});
|
2014-06-19 05:40:16 +00:00
|
|
|
|
2014-06-25 17:11:29 +00:00
|
|
|
/**
|
|
|
|
* if the buffer should loop once it's over
|
|
|
|
* @type {boolean}
|
2015-02-04 15:15:06 +00:00
|
|
|
* @private
|
2014-06-25 17:11:29 +00:00
|
|
|
*/
|
2015-02-04 15:15:06 +00:00
|
|
|
this._loop = options.loop;
|
2014-06-25 17:11:29 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* if 'loop' is true, the loop will start at this position
|
2015-06-14 00:20:36 +00:00
|
|
|
* @type {Time}
|
2015-02-27 16:19:45 +00:00
|
|
|
* @private
|
2014-06-25 17:11:29 +00:00
|
|
|
*/
|
2015-02-02 01:02:13 +00:00
|
|
|
this._loopStart = options.loopStart;
|
2014-06-25 17:11:29 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* if 'loop' is true, the loop will end at this position
|
2015-06-14 00:20:36 +00:00
|
|
|
* @type {Time}
|
2015-02-27 16:19:45 +00:00
|
|
|
* @private
|
2014-06-25 17:11:29 +00:00
|
|
|
*/
|
2015-02-02 01:02:13 +00:00
|
|
|
this._loopEnd = options.loopEnd;
|
2014-06-25 17:11:29 +00:00
|
|
|
|
2014-06-19 05:40:16 +00:00
|
|
|
/**
|
|
|
|
* the playback rate
|
|
|
|
* @private
|
|
|
|
* @type {number}
|
|
|
|
*/
|
2015-02-10 16:37:11 +00:00
|
|
|
this._playbackRate = options.playbackRate;
|
2014-06-19 05:40:16 +00:00
|
|
|
|
|
|
|
/**
|
2015-02-27 16:19:45 +00:00
|
|
|
* Enabling retrigger will allow a player to be restarted
|
|
|
|
* before the the previous 'start' is done playing.
|
2014-06-19 05:40:16 +00:00
|
|
|
* @type {boolean}
|
|
|
|
*/
|
2014-10-02 17:21:22 +00:00
|
|
|
this.retrigger = options.retrigger;
|
2014-06-18 21:01:39 +00:00
|
|
|
};
|
2014-04-06 00:47:59 +00:00
|
|
|
|
2014-06-19 05:40:16 +00:00
|
|
|
Tone.extend(Tone.Player, Tone.Source);
|
2014-09-30 04:28:48 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* the default parameters
|
|
|
|
* @static
|
|
|
|
* @const
|
|
|
|
* @type {Object}
|
|
|
|
*/
|
|
|
|
Tone.Player.defaults = {
|
2015-01-06 02:55:57 +00:00
|
|
|
"onload" : function(){},
|
2015-02-10 16:37:11 +00:00
|
|
|
"playbackRate" : 1,
|
2014-10-02 17:21:22 +00:00
|
|
|
"loop" : false,
|
2015-04-28 20:17:54 +00:00
|
|
|
"autostart" : false,
|
2014-10-02 17:21:22 +00:00
|
|
|
"loopStart" : 0,
|
2015-02-02 01:02:13 +00:00
|
|
|
"loopEnd" : 0,
|
2015-02-27 16:19:45 +00:00
|
|
|
"retrigger" : false,
|
2015-03-26 14:51:44 +00:00
|
|
|
"reverse" : false,
|
2014-09-30 04:28:48 +00:00
|
|
|
};
|
|
|
|
|
2014-06-18 21:01:39 +00:00
|
|
|
/**
|
2014-06-18 19:42:08 +00:00
|
|
|
* Load the audio file as an audio buffer.
|
|
|
|
* Decodes the audio asynchronously and invokes
|
2015-02-27 16:19:45 +00:00
|
|
|
* the callback once the audio buffer loads.
|
|
|
|
* Note: this does not need to be called, if a url
|
|
|
|
* was passed in to the constructor. Only use this
|
|
|
|
* if you want to manually load a new url.
|
|
|
|
* @param {string} url The url of the buffer to load.
|
|
|
|
* filetype support depends on the
|
|
|
|
* browser.
|
|
|
|
* @param {function(Tone.Player)=} callback
|
2015-06-14 00:54:29 +00:00
|
|
|
* @returns {Tone.Player} this
|
2014-06-18 21:01:39 +00:00
|
|
|
*/
|
2014-06-21 22:39:01 +00:00
|
|
|
Tone.Player.prototype.load = function(url, callback){
|
2015-04-28 20:17:54 +00:00
|
|
|
this._buffer.load(url, this._onload.bind(this, callback));
|
2015-02-01 19:40:47 +00:00
|
|
|
return this;
|
2014-06-18 21:01:39 +00:00
|
|
|
};
|
2014-04-06 00:47:59 +00:00
|
|
|
|
2015-04-28 20:17:54 +00:00
|
|
|
/**
|
|
|
|
* Internal callback when the buffer is loaded.
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Tone.Player.prototype._onload = function(callback){
|
|
|
|
callback(this);
|
|
|
|
if (this.autostart){
|
|
|
|
this.start();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-06-18 21:01:39 +00:00
|
|
|
/**
|
|
|
|
* play the buffer between the desired positions
|
2015-02-02 02:32:07 +00:00
|
|
|
*
|
|
|
|
* @private
|
2015-06-14 00:20:36 +00:00
|
|
|
* @param {Time} [startTime=now] when the player should start.
|
|
|
|
* @param {Time} [offset=0] the offset from the beginning of the sample
|
2014-12-03 22:20:23 +00:00
|
|
|
* to start at.
|
2015-06-14 00:20:36 +00:00
|
|
|
* @param {Time=} duration how long the sample should play. If no duration
|
2014-12-03 22:20:23 +00:00
|
|
|
* is given, it will default to the full length
|
|
|
|
* of the sample (minus any offset)
|
2015-06-14 00:54:29 +00:00
|
|
|
* @returns {Tone.Player} this
|
2014-06-18 21:01:39 +00:00
|
|
|
*/
|
2015-02-02 02:32:07 +00:00
|
|
|
Tone.Player.prototype._start = function(startTime, offset, duration){
|
|
|
|
if (this._buffer.loaded){
|
|
|
|
//if it's a loop the default offset is the loopstart point
|
2015-02-04 15:15:06 +00:00
|
|
|
if (this._loop){
|
2015-02-02 02:32:07 +00:00
|
|
|
offset = this.defaultArg(offset, this._loopStart);
|
|
|
|
} else {
|
|
|
|
//otherwise the default offset is 0
|
|
|
|
offset = this.defaultArg(offset, 0);
|
|
|
|
}
|
2015-04-28 20:17:54 +00:00
|
|
|
offset = this.toSeconds(offset);
|
2015-02-02 02:32:07 +00:00
|
|
|
duration = this.defaultArg(duration, this._buffer.duration - offset);
|
2015-02-23 05:29:49 +00:00
|
|
|
//the values in seconds
|
|
|
|
startTime = this.toSeconds(startTime);
|
|
|
|
duration = this.toSeconds(duration);
|
2015-02-02 02:32:07 +00:00
|
|
|
//make the source
|
|
|
|
this._source = this.context.createBufferSource();
|
|
|
|
this._source.buffer = this._buffer.get();
|
|
|
|
//set the looping properties
|
2015-02-04 15:15:06 +00:00
|
|
|
if (this._loop){
|
|
|
|
this._source.loop = this._loop;
|
2015-02-02 02:32:07 +00:00
|
|
|
this._source.loopStart = this.toSeconds(this._loopStart);
|
|
|
|
this._source.loopEnd = this.toSeconds(this._loopEnd);
|
2015-04-29 02:12:59 +00:00
|
|
|
// this fixes a bug in chrome 42 that breaks looping
|
|
|
|
// https://code.google.com/p/chromium/issues/detail?id=457099
|
|
|
|
duration = 65536;
|
2015-02-23 05:29:49 +00:00
|
|
|
} else {
|
|
|
|
this._nextStop = startTime + duration;
|
2014-06-19 05:40:16 +00:00
|
|
|
}
|
2015-02-02 02:32:07 +00:00
|
|
|
//and other properties
|
|
|
|
this._source.playbackRate.value = this._playbackRate;
|
2015-02-23 05:29:49 +00:00
|
|
|
this._source.onended = this.onended;
|
2015-02-02 02:32:07 +00:00
|
|
|
this._source.connect(this.output);
|
|
|
|
//start it
|
2015-02-23 05:29:49 +00:00
|
|
|
this._source.start(startTime, offset, duration);
|
2015-02-02 02:32:07 +00:00
|
|
|
} else {
|
|
|
|
throw Error("tried to start Player before the buffer was loaded");
|
2014-03-30 21:54:02 +00:00
|
|
|
}
|
2015-02-01 19:40:47 +00:00
|
|
|
return this;
|
2014-06-18 21:01:39 +00:00
|
|
|
};
|
2014-03-11 04:54:40 +00:00
|
|
|
|
2014-06-18 21:01:39 +00:00
|
|
|
/**
|
2014-06-18 19:42:08 +00:00
|
|
|
* Stop playback.
|
2015-02-02 02:32:07 +00:00
|
|
|
* @private
|
2015-06-14 00:20:36 +00:00
|
|
|
* @param {Time} [time=now]
|
2015-06-14 00:54:29 +00:00
|
|
|
* @returns {Tone.Player} this
|
2014-06-18 21:01:39 +00:00
|
|
|
*/
|
2015-02-02 02:32:07 +00:00
|
|
|
Tone.Player.prototype._stop = function(time){
|
|
|
|
if (this._source){
|
|
|
|
this._source.stop(this.toSeconds(time));
|
2015-02-23 05:29:49 +00:00
|
|
|
this._source = null;
|
2014-04-06 00:47:59 +00:00
|
|
|
}
|
2015-02-01 19:40:47 +00:00
|
|
|
return this;
|
2014-06-18 21:01:39 +00:00
|
|
|
};
|
2014-03-11 04:54:40 +00:00
|
|
|
|
2014-10-02 17:21:22 +00:00
|
|
|
/**
|
2015-06-14 01:54:20 +00:00
|
|
|
* Set the loop start and end. Will only loop if loop is
|
|
|
|
* set to true.
|
2015-06-14 00:20:36 +00:00
|
|
|
* @param {Time} loopStart The loop end time
|
|
|
|
* @param {Time} loopEnd The loop end time
|
2015-06-14 00:54:29 +00:00
|
|
|
* @returns {Tone.Player} this
|
2015-02-27 16:19:45 +00:00
|
|
|
* @example
|
2015-06-14 01:54:20 +00:00
|
|
|
* player.setLoopPoints(0.2, 0.3);
|
|
|
|
* player.loop = true;
|
2014-10-02 17:21:22 +00:00
|
|
|
*/
|
|
|
|
Tone.Player.prototype.setLoopPoints = function(loopStart, loopEnd){
|
2015-02-10 16:37:11 +00:00
|
|
|
this.loopStart = loopStart;
|
|
|
|
this.loopEnd = loopEnd;
|
2015-02-01 19:40:47 +00:00
|
|
|
return this;
|
2014-10-02 17:21:22 +00:00
|
|
|
};
|
|
|
|
|
2014-09-30 04:28:48 +00:00
|
|
|
/**
|
2015-06-14 01:54:20 +00:00
|
|
|
* If loop is true, the loop will start at this position.
|
2015-02-02 01:02:13 +00:00
|
|
|
* @memberOf Tone.Player#
|
2015-06-14 00:20:36 +00:00
|
|
|
* @type {Time}
|
2015-02-02 01:02:13 +00:00
|
|
|
* @name loopStart
|
2014-09-30 04:28:48 +00:00
|
|
|
*/
|
2015-02-04 15:15:06 +00:00
|
|
|
Object.defineProperty(Tone.Player.prototype, "loopStart", {
|
|
|
|
get : function(){
|
|
|
|
return this._loopStart;
|
|
|
|
},
|
|
|
|
set : function(loopStart){
|
|
|
|
this._loopStart = loopStart;
|
|
|
|
if (this._source){
|
|
|
|
this._source.loopStart = this.toSeconds(loopStart);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2015-02-02 01:02:13 +00:00
|
|
|
|
|
|
|
/**
|
2015-06-14 01:54:20 +00:00
|
|
|
* If loop is true, the loop will end at this position.
|
2015-02-02 01:02:13 +00:00
|
|
|
* @memberOf Tone.Player#
|
2015-06-14 00:20:36 +00:00
|
|
|
* @type {Time}
|
2015-02-02 01:02:13 +00:00
|
|
|
* @name loopEnd
|
|
|
|
*/
|
2015-02-04 15:15:06 +00:00
|
|
|
Object.defineProperty(Tone.Player.prototype, "loopEnd", {
|
|
|
|
get : function(){
|
|
|
|
return this._loopEnd;
|
|
|
|
},
|
|
|
|
set : function(loopEnd){
|
|
|
|
this._loopEnd = loopEnd;
|
|
|
|
if (this._source){
|
|
|
|
this._source.loopEnd = this.toSeconds(loopEnd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2015-02-02 01:02:13 +00:00
|
|
|
|
|
|
|
/**
|
2015-02-04 15:15:06 +00:00
|
|
|
* The audio buffer belonging to the player.
|
2015-02-02 01:02:13 +00:00
|
|
|
* @memberOf Tone.Player#
|
2015-02-04 15:15:06 +00:00
|
|
|
* @type {AudioBuffer}
|
|
|
|
* @name buffer
|
2015-02-02 01:02:13 +00:00
|
|
|
*/
|
2015-02-04 15:15:06 +00:00
|
|
|
Object.defineProperty(Tone.Player.prototype, "buffer", {
|
|
|
|
get : function(){
|
|
|
|
return this._buffer;
|
|
|
|
},
|
|
|
|
set : function(buffer){
|
|
|
|
this._buffer.set(buffer);
|
|
|
|
}
|
|
|
|
});
|
2015-02-02 01:02:13 +00:00
|
|
|
|
|
|
|
/**
|
2015-02-27 16:19:45 +00:00
|
|
|
* If the buffer should loop once it's over.
|
2015-02-02 01:02:13 +00:00
|
|
|
* @memberOf Tone.Player#
|
2015-02-04 15:15:06 +00:00
|
|
|
* @type {boolean}
|
|
|
|
* @name loop
|
2015-02-02 01:02:13 +00:00
|
|
|
*/
|
2015-02-04 15:15:06 +00:00
|
|
|
Object.defineProperty(Tone.Player.prototype, "loop", {
|
|
|
|
get : function(){
|
|
|
|
return this._loop;
|
|
|
|
},
|
|
|
|
set : function(loop){
|
|
|
|
this._loop = loop;
|
|
|
|
if (this._source){
|
|
|
|
this._source.loop = loop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The playback speed. 1 is normal speed.
|
2015-02-10 16:37:11 +00:00
|
|
|
* Note that this is not a Tone.Signal because of a bug in Blink.
|
2015-06-14 01:54:20 +00:00
|
|
|
* Please star <a href="https://code.google.com/p/chromium/issues/detail?id=311284">this</a>
|
|
|
|
* issue if this an important thing to you.
|
2015-02-04 15:15:06 +00:00
|
|
|
* @memberOf Tone.Player#
|
|
|
|
* @type {number}
|
|
|
|
* @name playbackRate
|
|
|
|
*/
|
|
|
|
Object.defineProperty(Tone.Player.prototype, "playbackRate", {
|
|
|
|
get : function(){
|
|
|
|
return this._playbackRate;
|
|
|
|
},
|
|
|
|
set : function(rate){
|
|
|
|
this._playbackRate = rate;
|
|
|
|
if (this._source) {
|
|
|
|
this._source.playbackRate.value = rate;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2014-09-30 04:28:48 +00:00
|
|
|
|
2015-03-26 14:51:44 +00:00
|
|
|
/**
|
|
|
|
* The direction the buffer should play in
|
|
|
|
* @memberOf Tone.Player#
|
|
|
|
* @type {boolean}
|
|
|
|
* @name reverse
|
|
|
|
*/
|
|
|
|
Object.defineProperty(Tone.Player.prototype, "reverse", {
|
|
|
|
get : function(){
|
|
|
|
return this._buffer.reverse;
|
|
|
|
},
|
|
|
|
set : function(rev){
|
|
|
|
this._buffer.reverse = rev;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2014-06-19 05:40:16 +00:00
|
|
|
/**
|
2015-06-14 02:30:33 +00:00
|
|
|
* Dispose and disconnect.
|
2015-06-14 00:54:29 +00:00
|
|
|
* @return {Tone.Player} this
|
2014-06-19 05:40:16 +00:00
|
|
|
*/
|
2015-02-02 03:05:24 +00:00
|
|
|
Tone.Player.prototype.dispose = function(){
|
|
|
|
Tone.Source.prototype.dispose.call(this);
|
2014-06-20 04:25:10 +00:00
|
|
|
if (this._source !== null){
|
2014-06-19 05:40:16 +00:00
|
|
|
this._source.disconnect();
|
|
|
|
this._source = null;
|
|
|
|
}
|
2015-01-05 02:46:10 +00:00
|
|
|
this._buffer.dispose();
|
2015-04-05 18:41:43 +00:00
|
|
|
this._buffer = null;
|
2015-02-02 03:05:24 +00:00
|
|
|
return this;
|
2014-06-18 21:01:39 +00:00
|
|
|
};
|
2014-03-24 17:41:08 +00:00
|
|
|
|
2014-04-06 00:47:59 +00:00
|
|
|
return Tone.Player;
|
|
|
|
});
|