UNPKG

84.4 kBHTMLView Raw
1<!DOCTYPE html>
2
3<html lang="en">
4<head>
5 <meta charset="utf-8">
6 <meta name="viewport" content="width=device-width">
7 <title>CrossBrowdy API documentation Source: CrossBase/audiovisual/audio/CB_AudioFile_API_SM2.js</title>
8
9 <!--[if lt IE 9]>
10 <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
11 <![endif]-->
12 <link type="text/css" rel="stylesheet" href="styles/sunlight.default.css">
13
14 <link type="text/css" rel="stylesheet" href="styles/site.cosmo.css">
15
16</head>
17
18<body style="min-width:800px; overflow-wrap:break-word; word-wrap:break-word; word-break:break-word; line-break:strict; hyphens:none; -webkit-hyphens:none; -moz-hyphens:none;">
19
20<div class="navbar navbar-default navbar-fixed-top ">
21<div class="container">
22 <div class="navbar-header">
23 <a class="navbar-brand" href="index.html">CrossBrowdy API documentation</a>
24 <button class="navbar-toggle" type="button" data-toggle="collapse" data-target="#topNavigation">
25 <span class="icon-bar"></span>
26 <span class="icon-bar"></span>
27 <span class="icon-bar"></span>
28 </button>
29 </div>
30 <div class="navbar-collapse collapse" id="topNavigation">
31 <ul class="nav navbar-nav">
32
33 <li class="dropdown">
34 <a href="namespaces.list.html" class="dropdown-toggle" data-toggle="dropdown">Namespaces<b class="caret"></b></a>
35 <ul class="dropdown-menu inline">
36 <li><a href="CB_Arrays.html">CB_Arrays</a></li><li><a href="CB_AudioDetector.html">CB_AudioDetector</a></li><li><a href="CB_baseSymbols.html">CB_baseSymbols</a></li><li><a href="CB_Client.html">CB_Client</a></li><li><a href="CB_Collisions.html">CB_Collisions</a></li><li><a href="CB_Configuration.html">CB_Configuration</a></li><li><a href="CB_Configuration.CrossBase.html">CB_Configuration.CrossBase</a></li><li><a href="CB_Configuration.CrossBrowdy.html">CB_Configuration.CrossBrowdy</a></li><li><a href="CB_Controllers.html">CB_Controllers</a></li><li><a href="CB_Controllers_Proprietary.html">CB_Controllers_Proprietary</a></li><li><a href="CB_Controllers_Proprietary.WII.html">CB_Controllers_Proprietary.WII</a></li><li><a href="CB_Controllers_Proprietary.WII_U.html">CB_Controllers_Proprietary.WII_U</a></li><li><a href="CB_Device.html">CB_Device</a></li><li><a href="CB_Device.AmbientLight.html">CB_Device.AmbientLight</a></li><li><a href="CB_Device.Battery.html">CB_Device.Battery</a></li><li><a href="CB_Device.Location.html">CB_Device.Location</a></li><li><a href="CB_Device.Motion.html">CB_Device.Motion</a></li><li><a href="CB_Device.Orientation.html">CB_Device.Orientation</a></li><li><a href="CB_Device.Proximity.html">CB_Device.Proximity</a></li><li><a href="CB_Device.Vibration.html">CB_Device.Vibration</a></li><li><a href="CB_Elements.html">CB_Elements</a></li><li><a href="CB_Events.html">CB_Events</a></li><li><a href="CB_Keyboard.html">CB_Keyboard</a></li><li><a href="CB_Keyboard.chars.html">CB_Keyboard.chars</a></li><li><a href="CB_Keyboard.extended.html">CB_Keyboard.extended</a></li><li><a href="CB_Keyboard.keys.html">CB_Keyboard.keys</a></li><li><a href="CB_Modules.html">CB_Modules</a></li><li><a href="CB_Mouse.html">CB_Mouse</a></li><li><a href="CB_Mouse.CursorImage.html">CB_Mouse.CursorImage</a></li><li><a href="CB_Net.html">CB_Net</a></li><li><a href="CB_Net.Fetch.html">CB_Net.Fetch</a></li><li><a href="CB_Net.REST.html">CB_Net.REST</a></li><li><a href="CB_Net.Sockets.html">CB_Net.Sockets</a></li><li><a href="CB_Net.Sockets.SockJS.html">CB_Net.Sockets.SockJS</a></li><li><a href="CB_Net.XHR.html">CB_Net.XHR</a></li><li><a href="CB_Pointer.html">CB_Pointer</a></li><li><a href="CB_Screen.html">CB_Screen</a></li><li><a href="CB_Speaker.html">CB_Speaker</a></li><li><a href="CB_Touch.html">CB_Touch</a></li>
37 </ul>
38 </li>
39
40 <li class="dropdown">
41 <a href="classes.list.html" class="dropdown-toggle" data-toggle="dropdown">Classes<b class="caret"></b></a>
42 <ul class="dropdown-menu inline">
43 <li><a href="CB_AudioFile.html">CB_AudioFile</a></li><li><a href="CB_AudioFile_API.AAPI.html">CB_AudioFile_API.AAPI</a></li><li><a href="CB_AudioFile_API.ACMP.html">CB_AudioFile_API.ACMP</a></li><li><a href="CB_AudioFile_API.SM2.html">CB_AudioFile_API.SM2</a></li><li><a href="CB_AudioFile_API.WAAPI.html">CB_AudioFile_API.WAAPI</a></li><li><a href="CB_AudioFileCache.html">CB_AudioFileCache</a></li><li><a href="CB_AudioFileSprites.html">CB_AudioFileSprites</a></li><li><a href="CB_AudioFileSpritesPool.html">CB_AudioFileSpritesPool</a></li><li><a href="CB_Canvas.html">CB_Canvas</a></li><li><a href="CB_GraphicSprites.html">CB_GraphicSprites</a></li><li><a href="CB_GraphicSpritesScene.html">CB_GraphicSpritesScene</a></li>
44 </ul>
45 </li>
46
47 <li class="dropdown">
48 <a href="global.html" class="dropdown-toggle" data-toggle="dropdown">Global<b class="caret"></b></a>
49 <ul class="dropdown-menu inline">
50 <li><a href="global.html#CB_addCredits">CB_addCredits</a></li><li><a href="global.html#CB_BASE_NAME">CB_BASE_NAME</a></li><li><a href="global.html#CB_baseToBase">CB_baseToBase</a></li><li><a href="global.html#CB_baseToInt">CB_baseToInt</a></li><li><a href="global.html#CB_br2nl">CB_br2nl</a></li><li><a href="global.html#CB_brToNl">CB_brToNl</a></li><li><a href="global.html#CB_combineArraysOrObjects">CB_combineArraysOrObjects</a></li><li><a href="global.html#CB_combineAutomatically">CB_combineAutomatically</a></li><li><a href="global.html#CB_combineJSON">CB_combineJSON</a></li><li><a href="global.html#CB_combineURIParameters">CB_combineURIParameters</a></li><li><a href="global.html#CB_combineURLParameters">CB_combineURLParameters</a></li><li><a href="global.html#CB_console">CB_console</a></li><li><a href="global.html#CB_copyObject">CB_copyObject</a></li><li><a href="global.html#CB_countDecimalDigits">CB_countDecimalDigits</a></li><li><a href="global.html#CB_countDecimalPart">CB_countDecimalPart</a></li><li><a href="global.html#CB_countDecimals">CB_countDecimals</a></li><li><a href="global.html#CB_countIntegerDigits">CB_countIntegerDigits</a></li><li><a href="global.html#CB_countIntegerPart">CB_countIntegerPart</a></li><li><a href="global.html#CB_credits">CB_credits</a></li><li><a href="global.html#CB_CREDITS_DEFAULT">CB_CREDITS_DEFAULT</a></li><li><a href="global.html#CB_forceString">CB_forceString</a></li><li><a href="global.html#CB_forEach">CB_forEach</a></li><li><a href="global.html#CB_getBase64StringObject">CB_getBase64StringObject</a></li><li><a href="global.html#CB_getCookie">CB_getCookie</a></li><li><a href="global.html#CB_getDatum">CB_getDatum</a></li><li><a href="global.html#CB_getJSONPropertyValue">CB_getJSONPropertyValue</a></li><li><a href="global.html#CB_getLZStringObject">CB_getLZStringObject</a></li><li><a href="global.html#CB_getValueIndex">CB_getValueIndex</a></li><li><a href="global.html#CB_getValuePath">CB_getValuePath</a></li><li><a href="global.html#CB_includeJSFile">CB_includeJSFile</a></li><li><a href="global.html#CB_indexOf">CB_indexOf</a></li><li><a href="global.html#CB_init">CB_init</a></li><li><a href="global.html#CB_intToBase">CB_intToBase</a></li><li><a href="global.html#CB_isArray">CB_isArray</a></li><li><a href="global.html#CB_isEmail">CB_isEmail</a></li><li><a href="global.html#CB_isFileLocal">CB_isFileLocal</a></li><li><a href="global.html#CB_isString">CB_isString</a></li><li><a href="global.html#CB_lastIndexOf">CB_lastIndexOf</a></li><li><a href="global.html#CB_ltrim">CB_ltrim</a></li><li><a href="global.html#CB_NAME">CB_NAME</a></li><li><a href="global.html#CB_nl2br">CB_nl2br</a></li><li><a href="global.html#CB_nlToBr">CB_nlToBr</a></li><li><a href="global.html#CB_numberFormat">CB_numberFormat</a></li><li><a href="global.html#CB_numberOfDecimalDigits">CB_numberOfDecimalDigits</a></li><li><a href="global.html#CB_numberOfDecimals">CB_numberOfDecimals</a></li><li><a href="global.html#CB_numberOfIntegerDigits">CB_numberOfIntegerDigits</a></li><li><a href="global.html#CB_OPTIONS">CB_OPTIONS</a></li><li><a href="global.html#CB_parseJSON">CB_parseJSON</a></li><li><a href="global.html#CB_parseString">CB_parseString</a></li><li><a href="global.html#CB_regularExpressionString">CB_regularExpressionString</a></li><li><a href="global.html#CB_renderString">CB_renderString</a></li><li><a href="global.html#CB_replaceAll">CB_replaceAll</a></li><li><a href="global.html#CB_rtrim">CB_rtrim</a></li><li><a href="global.html#CB_scriptPath">CB_scriptPath</a></li><li><a href="global.html#CB_scriptPathCalculate">CB_scriptPathCalculate</a></li><li><a href="global.html#CB_setCookie">CB_setCookie</a></li><li><a href="global.html#CB_setDatum">CB_setDatum</a></li><li><a href="global.html#CB_sizeof">CB_sizeof</a></li><li><a href="global.html#CB_sizeOf">CB_sizeOf</a></li><li><a href="global.html#CB_stringifyJSON">CB_stringifyJSON</a></li><li><a href="global.html#CB_symmetricCall">CB_symmetricCall</a></li><li><a href="global.html#CB_symmetricCallClear">CB_symmetricCallClear</a></li><li><a href="global.html#CB_this">CB_this</a></li><li><a href="global.html#CB_trim">CB_trim</a></li><li><a href="global.html#CB_VERSION">CB_VERSION</a></li>
51 </ul>
52 </li>
53
54 </ul>
55
56 <div class="col-sm-3 col-md-3">
57 <form class="navbar-form" role="search">
58 <div class="input-group">
59 <input type="text" class="form-control" placeholder="Search" name="q" id="search-input">
60 <div class="input-group-btn">
61 <button class="btn btn-default" id="search-submit"><i class="glyphicon glyphicon-search"></i></button>
62 </div>
63 </div>
64 </form>
65 </div>
66
67 </div>
68
69</div>
70</div>
71
72
73<div class="container" id="toc-content" style="width:100%;">
74<div class="row" style="width:100%;">
75
76
77 <div class="col-md-12">
78
79 <div id="main">
80
81
82 <h1 class="page-title">Source: CrossBase/audiovisual/audio/CB_AudioFile_API_SM2.js</h1>
83
84<section>
85 <article>
86 <pre
87 class="sunlight-highlight-javascript linenums">/**
88 * @file Audio files management using "SM2" ([SoundManager 2]{@link http://schillmania.com/projects/soundmanager2/}). Contains the {@link CB_AudioFile_API.SM2} class.
89 * @author Joan Alba Maldonado &lt;workindalian@gmail.com>
90 * @license Creative Commons Attribution 4.0 International. See more at {@link https://crossbrowdy.com/about#what_is_the_crossbrowdy_copyright_and_license}.
91 */
92
93
94//We need a limit to prevent Firefox error ("Media resource [URI] could not be decoded") since many calls to play() method would fail:
95if (typeof(CB_AudioFile_API_AAPI_SM2_beingLoading) === "undefined") { var CB_AudioFile_API_AAPI_SM2_beingLoading = 0; } //Counts how many objects are loading.
96if (typeof(CB_AudioFile_API_AAPI_SM2_maximumLoading) === "undefined") { var CB_AudioFile_API_AAPI_SM2_maximumLoading = 5; } //Maximum of objects that can be loading at the same time.
97if (typeof(CB_AudioFile_API_AAPI_SM2_beingChecking) === "undefined") { var CB_AudioFile_API_AAPI_SM2_beingChecking = 0; } //Counts how many objects are loading.
98if (typeof(CB_AudioFile_API_AAPI_SM2_maximumChecking) === "undefined") { var CB_AudioFile_API_AAPI_SM2_maximumChecking = CB_AudioFile_API_AAPI_SM2_maximumLoading; } //Maximum of objects that can be loading at the same time.
99
100
101//Class to manage an audio file with SM2 (SoundManager 2):
102if (typeof(CB_AudioFile_API) === "undefined") { var CB_AudioFile_API = {}; }
103/**
104 * The constructor is recommended to be called through a user-driven event (as onClick, onTouch, etc.) if the "autoPlay" option is set to true, as some web clients may need this at least the first time in order to be able to play the audio.
105 * @class CB_AudioFile_API.SM2
106 * @memberof! &lt;global>
107 * @classdesc Class to manage an audio file using "SM2" ([SoundManager 2]{@link http://schillmania.com/projects/soundmanager2/}). Used by the {@link CB_AudioFile} class internally and it shares most of its properties and methods. Recommended for internal usage only.
108 * @param {string} filePath - The path of the audio file or a data URI. NOTE: Only some clients with some audio APIs will support data URIs.
109 * @param {string} [audioId='CB_AUDIOFILE_SM2_' + CB_AudioFile_API.SM2._idUnique++] - Desired identifier for the audio object. If not provided, an automatic unique ID will be calculated. Note that it is not case sensitive and it should be unique for each object.
110 * @param {CB_AudioFile_API.SM2.OPTIONS} [options=CB_AudioFile_API.SM2#DEFAULT_OPTIONS] - Object with the desired options.
111 * @param {function} [callbackOk] - Function with no parameters to be called when the audio has been loaded successfully, being "this" the {@link CB_AudioFile_API.SM2} object itself.
112 * @param {function} [callbackError] - Function to be called if the audio has not been loaded successfully. The first and unique parameter will be a string describing the error found (if could be determined), being "this" the {@link CB_AudioFile_API.SM2} object itself.
113 * @returns {CB_AudioFile_API.SM2} Returns a new {@link CB_AudioFile_API.SM2} object.
114 * @todo Do not allow to create one object with an "id" which has already been used (unless the value is undefined, null...). Note that the "id" is not case sensitive and it should be unique for each object.
115 * @todo Method getCopy and static method filterProperties (similar to the ones from {@link CB_GraphicSprites} and {@link CB_GraphicSpritesScene}).
116 */
117CB_AudioFile_API["SM2"] = function(filePath, audioId, options, callbackOk, callbackError)
118{
119 //Creates an instance of this object and returns it in the case that it is being called from an unexpected context:
120 if (this === window || !(this instanceof CB_AudioFile_API["SM2"])) { return new CB_AudioFile_API["SM2"](filePath, audioId, options, callbackOk, callbackError); }
121
122 //Constants:
123 /**
124 * Keeps the default volume. If the {@link CB_Configuration.CrossBase.CB_AudioFile_AudioFileCache_USE_SPEAKER_VOLUME_AS_DEFAULT} property is true, this will keep the result of calling the {@link CB_Speaker.getVolume} function. Otherwise, it will use the value of the {@link CB_Configuration.CrossBase.CB_Speaker_DEFAULT_VOLUME} variable.
125 * @constant CB_AudioFile_API.SM2#DEFAULT_VOLUME
126 * @type {number}
127 * @default CB_Configuration.CrossBase.CB_AudioFile_AudioFileCache_USE_SPEAKER_VOLUME_AS_DEFAULT ? CB_Speaker.getVolume() : CB_Configuration.CrossBase.CB_Speaker_DEFAULT_VOLUME
128 */
129 CB_AudioFile_API["SM2"].prototype.DEFAULT_VOLUME = CB_Configuration[CB_BASE_NAME].CB_AudioFile_AudioFileCache_USE_SPEAKER_VOLUME_AS_DEFAULT ? CB_Speaker.getVolume() : CB_Configuration[CB_BASE_NAME].CB_Speaker_DEFAULT_VOLUME;
130
131 /**
132 * Keeps the default options when an object is created. Format: { autoLoad: boolean, autoPlay: boolean, loop: boolean, volume: number }.
133 * @constant CB_AudioFile_API.SM2#DEFAULT_OPTIONS
134 * @type {CB_AudioFile_API.SM2.OPTIONS}
135 * @default { autoLoad: true, autoPlay: false, loop: false, volume: [CB_AudioFile_API.SM2.prototype.DEFAULT_VOLUME]{@link CB_AudioFile_API.SM2#DEFAULT_VOLUME} }
136 */
137 CB_AudioFile_API["SM2"].prototype.DEFAULT_OPTIONS = { autoLoad: true, autoPlay: false, loop: false, volume: CB_AudioFile_API["SM2"].prototype.DEFAULT_VOLUME };
138
139 //Properties and variables:
140 /**
141 * Tells whether the file is unloaded ({@link CB_AudioFile.UNLOADED}), loading ({@link CB_AudioFile.LOADING}), unchecked ({@link CB_AudioFile.UNCHECKED}), checking ({@link CB_AudioFile.CHECKING}), loaded ({@link CB_AudioFile.LOADED}), failed ({@link CB_AudioFile.FAILED}) or aborted ({@link CB_AudioFile.ABORTED}).
142 * @var CB_AudioFile_API.SM2#status
143 * @readonly
144 * @type {integer}
145 * @default {@link CB_AudioFile.UNLOADED}
146 */
147 this.status = CB_AudioFile.UNLOADED;
148
149 /**
150 * Defines whether the file loops by default when the audio is played or not. Its value will be modified automatically whenever the {@link CB_AudioFile_API.SM2#play} method is called, getting the value from the "loop" parameter (but only if contains a boolean).
151 * @var CB_AudioFile_API.SM2#loop
152 * @readonly
153 * @type {boolean}
154 * @default [CB_AudioFile_API.SM2.prototype.DEFAULT_OPTIONS]{@link CB_AudioFile_API.SM2#DEFAULT_OPTIONS}.loop
155 */
156 this.loop = CB_AudioFile_API["SM2"].prototype.DEFAULT_OPTIONS.loop;
157
158 /**
159 * Stores the volume of this audio. Accepted values go from 0 to MAX_VOLUME, where MAX_VOLUME is 100 if the {@link CB_Configuration.CrossBase.CB_AudioFile_AudioFileCache_USE_SPEAKER_VOLUME_AS_MAXIMUM} property is false or otherwise MAX_VOLUME is the returning value of the {@link CB_Speaker.getVolume} function.
160 * @var CB_AudioFile_API.SM2#volume
161 * @readonly
162 * @type {number}
163 * @default [CB_AudioFile_API.SM2.prototype.DEFAULT_OPTIONS]{@link CB_AudioFile_API.SM2#DEFAULT_OPTIONS}.volume
164 */
165 this.volume = CB_AudioFile_API["SM2"].prototype.DEFAULT_OPTIONS.volume;
166
167 /**
168 * Stores the volume of this audio before it was muted (to restore it later). Valid values go from 0 to MAX_VOLUME, where MAX_VOLUME is 100 if the {@link CB_Configuration.CrossBase.CB_AudioFile_AudioFileCache_USE_SPEAKER_VOLUME_AS_MAXIMUM} property is false or otherwise MAX_VOLUME is the returning value of the {@link CB_Speaker.getVolume} function.
169 * @var CB_AudioFile_API.SM2#volumeBeforeMute
170 * @readonly
171 * @type {number}
172 * @default {@link CB_AudioFile_API.SM2#volume}
173 */
174 this.volumeBeforeMute = this.volume;
175
176 /**
177 * Stores the identifier for the audio file.
178 * @var CB_AudioFile_API.SM2#id
179 * @readonly
180 * @type {string}
181 * @default 'CB_AUDIOFILE_SM2_' + CB_AudioFile_API.SM2._idUnique++
182 */
183 this.id = "";
184
185 /**
186 * Stores the path of the audio file or the data URI. NOTE: Only some clients with some audio APIs will support data URIs.
187 * @var CB_AudioFile_API.SM2#filePath
188 * @readonly
189 * @type {string}
190 * @default
191 */
192 this.filePath = "";
193
194 /**
195 * Tells whether the audio is paused or not.
196 * @var CB_AudioFile_API.SM2#paused
197 * @readonly
198 * @type {boolean}
199 * @default false
200 */
201 this.paused = false;
202
203 /**
204 * Stores the time (in milliseconds) when the audio has been paused.
205 * @var CB_AudioFile_API.SM2#pauseTime
206 * @readonly
207 * @type {number}
208 * @default
209 */
210 this.pauseTime = 0;
211
212 /**
213 * Tells whether the audio is stopped or not.
214 * @var CB_AudioFile_API.SM2#stopped
215 * @readonly
216 * @type {boolean}
217 * @default true
218 */
219 this.stopped = true;
220
221 /**
222 * Function to call when the audio stops.
223 * @var CB_AudioFile_API.SM2#onStopFunction
224 * @readonly
225 * @type {function}
226 * @default
227 */
228 this.onStopFunction = null;
229
230 /**
231 * Stores the last "startAt" parameter value used by the {@link CB_AudioFile_API.SM2#play} or the {@link CB_AudioFile_API.SM2#resume} method.
232 * @var CB_AudioFile_API.SM2#lastStartAt
233 * @readonly
234 * @type {number}
235 * @default
236 */
237 this.lastStartAt = null;
238
239 /**
240 * Stores the last "stopAt" parameter value used by the {@link CB_AudioFile_API.SM2#play} or the {@link CB_AudioFile_API.SM2#resume} method.
241 * @var CB_AudioFile_API.SM2#lastStopAt
242 * @readonly
243 * @type {number}
244 * @default
245 */
246 this.lastStopAt = null;
247
248 /**
249 * Stores the "SMSound" object (returned by the [createSound]{@link http://schillmania.com/projects/soundmanager2/doc/#soundmanager-createsound} method) of the audio, used by the "SM2" ([SoundManager 2]{@link http://schillmania.com/projects/soundmanager2/}).
250 * @var CB_AudioFile_API.SM2#soundObject
251 * @readonly
252 * @type {Object}
253 * @default
254 */
255 this.soundObject = null;
256
257
258 //Internal properties:
259 this._timeoutWhenStop = null; //Keeps the timeout that is executed when the audio has finished playing (to either stop or loop).
260 this._id_internal = null; //Internal id.
261 //this._resuming = false;
262 this._recursiveCallTimeout = null;
263 this._onReadyTimeout = null;
264 this._onErrorFunctionTimeout = null;
265 this._stopAfterPlayingTimeout = null;
266 this._onLoadTimeout = null;
267 this._loadingCounterIncreased = false;
268
269 this._checkPlayingTimeout = null;
270 this._checkPlayingFinishingTimeout = null;
271 this._recursiveCallCheckingTimeout = null;
272 this._checkingCounterIncreased = false;
273
274 this._lastDuration = null;
275 this._onPlayingErrorFunctionTimeout = null;
276 this._onPlayingErrorFunctionExecuting = false;
277 this._startPlayingTimeReal = 0;
278
279
280 //Calls the constructor of the object when creates an instance:
281 return this._init(filePath, audioId, options, callbackOk, callbackError);
282}
283
284
285/**
286 * Object with the options for an audio file. The format is the following one: { autoLoad: boolean, autoPlay: boolean, loop: boolean, volume: number }.
287 * @memberof CB_AudioFile_API.SM2
288 * @typedef {Object} CB_AudioFile_API.SM2.OPTIONS
289 * @property {boolean} [autoLoad={@link CB_AudioFile_API.SM2#DEFAULT_OPTIONS}.autoLoad] - If set to false, it will not call the {@link CB_AudioFile_API.SM2#load} method internally when the constructor is called (not recommended).
290 * @property {boolean} [autoPlay={@link CB_AudioFile_API.SM2#DEFAULT_OPTIONS}.autoPlay] - Value which will be used as the "autoPlay" parameter when calling the {@link CB_AudioFile_API.SM2#load} method internally, only when the "autoLoad" is set to true (when the constructor is called).
291 * @property {boolean} [loop={@link CB_AudioFile_API.SM2#DEFAULT_OPTIONS}.loop] - Value that will be used for the {@link CB_AudioFile_API.SM2#loop} property.
292 * @property {number} [volume={@link CB_AudioFile_API.SM2#DEFAULT_OPTIONS}.volume] - The desired volume (from 0 to the maximum value, where the maximum value will be the returning value of calling the {@link CB_Speaker.getVolume} function if the {@link CB_Configuration.CrossBase.CB_AudioFile_AudioFileCache_USE_SPEAKER_VOLUME_AS_MAXIMUM} property is set to true or it will be 100 otherwise) that will be used for the {@link CB_AudioFile_API.SM2#volume} property.
293 */
294
295
296//Static properties:
297CB_AudioFile_API["SM2"]._counter = 0; //Internal counter.
298CB_AudioFile_API["SM2"]._idUnique = 0; //Counter to make the id unique (otherwise SM2 will never create an object with the same id after destroying it).
299
300
301//Constructor:
302CB_AudioFile_API["SM2"].prototype._init = function(filePath, audioId, options, callbackOk, callbackError)
303{
304 //If not given, defines the default parameters:
305 if (typeof(audioId) === "undefined" || audioId === null) { audioId = "CB_AUDIOFILE_SM2_" + CB_AudioFile_API["SM2"]._idUnique++; } //Uses the file path as default id.
306 if (typeof(options) === "undefined" || options === null) { options = this.DEFAULT_OPTIONS; }
307 else
308 {
309 if (typeof(options.loop) === "undefined" || options.loop === null) { options.loop = this.DEFAULT_OPTIONS.loop; }
310 if (typeof(options.autoLoad) === "undefined" || options.autoLoad === null) { options.autoLoad = this.DEFAULT_OPTIONS.autoLoad; }
311 if (typeof(options.autoPlay) === "undefined" || options.autoPlay === null) { options.autoPlay = this.DEFAULT_OPTIONS.autoPlay; }
312 if (typeof(options.volume) === "undefined" || options.volume === null) { options.volume = this.DEFAULT_OPTIONS.volume; }
313 }
314
315 //Sets the audio ID:
316 this.id = CB_trim(audioId).toUpperCase();
317
318 //Sets the internal id:
319 if (typeof(this._id_internal) === "undefined" || this._id_internal === null) { this._id_internal = CB_AudioFile_API["SM2"]._counter++; }
320
321 //Sets the file path:
322 this.filePath = filePath;
323
324 //Proceeds according to the options sent:
325 this.loop = options.loop;
326 this.volume = options.volume;
327 this.volumeBeforeMute = this.volume;
328 if (options.autoLoad)
329 {
330 var that = this;
331 setTimeout
332 (
333 function()
334 {
335 that.load(that.filePath, options.autoPlay, callbackOk, callbackError);
336 },
337 10
338 );
339 }
340
341 //Returns the object:
342 return this;
343}
344
345
346/**
347 * Destroys the audio file object and frees memory. Sets its current {@link CB_AudioFile_API.SM2#status} property to ABORTED ({@link CB_AudioFile.ABORTED} value).
348 * @function CB_AudioFile_API.SM2#destructor
349 * @param {boolean} [stopSound=false] - If set to true, it will also call the {@link CB_AudioFile_API.SM2#stop} method.
350 * @param {boolean} [keepStoppedUnaltered=false] - Used internally as the "keepStoppedUnaltered" parameter to call the {@link CB_AudioFile_API.SM2#stop} method. If the "stopSound" parameter is not set to true, this parameter will be ignored as the "stop" method will not be called.
351 * @param {boolean} [avoidOnStop=false] - Used internally as the "avoidOnStop" parameter to call the {@link CB_AudioFile_API.SM2#stop} method. If the "stopSound" parameter is not set to true, this parameter will be ignored as the "stop" method will not be called.
352 * @param {boolean} [forceOnStop=false] - Used internally as the "forceOnStop" parameter to call the {@link CB_AudioFile_API.SM2#stop} method. If the "stopSound" parameter is not set to true, this parameter will be ignored as the "stop" method will not be called.
353 */
354CB_AudioFile_API["SM2"].prototype.destructor = function(stopSound, keepStoppedUnaltered, avoidOnStop, forceOnStop)
355{
356 this._lastDuration = null;
357 if (typeof(this.soundObject) === "undefined" || this.soundObject === null) { this.status = CB_AudioFile.ABORTED; return; }
358 if (stopSound) { this.stop(keepStoppedUnaltered, avoidOnStop, forceOnStop); }
359
360 if (typeof(this.soundObject.destruct) !== "undefined" &amp;&amp; this.soundObject.destruct !== null)
361 {
362 this.soundObject.destruct();
363 }
364
365 CB_Elements.remove(this.soundObject);
366
367 //if (this.status === CB_AudioFile.LOADING)
368 if (this._loadingCounterIncreased)
369 {
370 this._loadingCounterIncreased = false;
371 CB_AudioFile_API_AAPI_SM2_beingLoading--; //Decreases the counter of the objects which are loading.
372 if (CB_AudioFile_API_AAPI_SM2_beingLoading &lt; 0) { CB_AudioFile_API_AAPI_SM2_beingLoading = 0; }
373 }
374
375 if (this._checkingCounterIncreased)
376 {
377 this._checkingCounterIncreased = false;
378 CB_AudioFile_API_AAPI_SM2_beingChecking--; //Decreases the counter of the objects which are checking.
379 if (CB_AudioFile_API_AAPI_SM2_beingChecking &lt; 0) { CB_AudioFile_API_AAPI_SM2_beingChecking = 0; }
380 }
381
382 this.status = CB_AudioFile.ABORTED;
383}
384
385
386/**
387 * Loads the desired audio file with the desired options. Recommended to be called through a user-driven event (as onClick, onTouch, etc.), as some web clients may need this at least the first time in order to be able to play the audio. This method will be called automatically by the constructor if the "autoLoad" option was set to true in its given "options" parameter.
388 * When this method is called, if the {@link CB_AudioFile_API.SM2#status} property already has the "LOADED" status (defined in the {@link CB_AudioFile.LOADED} constant) and the "forceReload" parameter is not set to true, it will exit calling the given "callbackOk" function (if any) immediately. Otherwise, regardless the status, the status will be set to "LOADING" (defined in the {@link CB_AudioFile.LOADING} constant). After it, it will reach the "UNCHECKED" (defined in the {@link CB_AudioFile.UNCHECKED} constant). If the "autoPlay" parameter is not set to true, this will be the final status (and it will be necessary to call the {@link CB_AudioFile_API.SM2#checkPlaying} method after it). After it and only if the "autoPlay" is set to true, as the {@link CB_AudioFile_API.SM2#checkPlaying} method will be called internally, it will have the "CHECKING" status (defined in the {@link CB_AudioFile.CHECKING} constant) and finally the "LOADED" status (defined in the {@link CB_AudioFile.LOADED} constant) if all goes well.
389 * @function CB_AudioFile_API.SM2#load
390 * @param {string} [filePath={@link CB_AudioFile_API.SM2#filePath}] - The path of the audio file or a data URI. NOTE: Only some clients with some audio APIs will support data URIs.
391 * @param {string} [autoPlay=false] - If set to true, it will start playing the audio automatically (by calling the {@link CB_AudioFile_API.SM2#play} method internally). If set to true and the {@link CB_AudioFile_API.SM2#status} property reaches to the "UNCHECKED" status (defined in the {@link CB_AudioFile.UNCHECKED} constant), it will also call internally the {@link CB_AudioFile_API.SM2#checkPlaying} method.
392 * @param {function} [callbackOk] - Function with no parameters to be called when the audio has been loaded successfully, being "this" the {@link CB_AudioFile_API.SM2} object itself.
393 * @param {function} [callbackError] - Function to be called if the audio has not been loaded successfully. The first and unique parameter will be a string describing the error found (if it could be determined), being "this" the {@link CB_AudioFile_API.SM2} object itself.
394 * @param {boolean} [forceReload=false] - If set to false, the "filePath" has not been changed from the previously used and the {@link CB_AudioFile_API.SM2#status} property belongs to the "LOADED" status (defined in the {@link CB_AudioFile.LOADED} constant), it will exit the method without loading the audio file again (calling the "callbackOk" function, if any).
395 * @returns {CB_AudioFile_API.SM2|null} Returns the audio API object (if it was possible to create) or null otherwise.
396 */
397CB_AudioFile_API["SM2"].prototype.load = function(filePath, autoPlay, callbackOk, callbackError, forceReload)
398{
399 clearTimeout(this._checkPlayingTimeout);
400 clearTimeout(this._onReadyTimeout);
401 clearTimeout(this._onErrorFunctionTimeout);
402 clearTimeout(this._stopAfterPlayingTimeout);
403 clearTimeout(this._onLoadTimeout);
404 clearTimeout(this._recursiveCallTimeout);
405 clearTimeout(this._checkPlayingFinishingTimeout);
406
407 filePath = filePath || this.filePath;
408
409 //If the status is LOADED and the file path give is the same as the current one, just exits:
410 if (!forceReload &amp;&amp; this.status === CB_AudioFile.LOADED &amp;&amp; this.filePath === filePath)
411 {
412 if (typeof(callbackOk) === "function") { callbackOk.call(this); }
413 return this;
414 }
415
416 this.status = CB_AudioFile.LOADING; //The file is loading.
417
418 var that = this;
419
420 //If the maximum of objects loading is reached, calls the function again after some time and exits:
421 //if (CB_AudioFile_API_AAPI_SM2_beingLoading >= CB_AudioFile_API_AAPI_SM2_maximumLoading)
422 if (autoPlay &amp;&amp; CB_AudioFile_API_AAPI_SM2_beingLoading >= CB_AudioFile_API_AAPI_SM2_maximumLoading)
423 {
424 this._recursiveCallTimeout = setTimeout(function() { that.load(filePath, autoPlay, callbackOk, callbackError, forceReload); }, 10);
425 return this;
426 }
427
428 if (typeof(this.soundObject) !== "undefined" &amp;&amp; this.soundObject !== null)
429 {
430 //this.soundObject.onload = function() { };
431 this.soundObject.onload = null;
432 }
433
434 //Destroys previous object (if any):
435 this.destructor(true, false, true); //Also stops the sound (if any) and prevents firing onStop.
436
437 this.status = CB_AudioFile.LOADING; //The file is loading.
438
439 if (!this._loadingCounterIncreased)
440 {
441 this._loadingCounterIncreased = true;
442 CB_AudioFile_API_AAPI_SM2_beingLoading++; //Increases the counter of the objects which are loading (destructor has decreased 1).
443 }
444
445 this.filePath = filePath;
446
447 var callbackFunctionError =
448 function(error, ignoreFailed)
449 {
450 //Prevents the execution of the function if the object has been declared as LOADED:
451 if (that.status === CB_AudioFile.LOADED || that.status === CB_AudioFile.UNCHECKED || that.status === CB_AudioFile.CHECKING) { return; }
452
453 if (that.status === CB_AudioFile.ABORTED) { return; } //If it is has been aborted, we exit.
454
455 //If it has already failed before, exits:
456 if (!ignoreFailed &amp;&amp; that.status === CB_AudioFile.FAILED) { return; }
457
458 if (that._loadingCounterIncreased)
459 {
460 that._loadingCounterIncreased = false;
461 CB_AudioFile_API_AAPI_SM2_beingLoading--; //Decreases the counter of the objects which are loading.
462 if (CB_AudioFile_API_AAPI_SM2_beingLoading &lt; 0) { CB_AudioFile_API_AAPI_SM2_beingLoading = 0; }
463 }
464
465 that.status = CB_AudioFile.FAILED; //File failed to load.
466 autoPlay = false;
467 //var fileName = filePath;
468 if (filePath.substring(0, 5).toLowerCase() === "data:") { filePath = filePath.substring(0, 15) + "[...]" + filePath.substring(filePath.length - 2); }
469 error = "Error message for " + filePath + " file: " + error;
470 if (typeof(callbackError) === "function") { callbackError.call(that, error); } //Calls the Error function back.
471 };
472
473 try
474 {
475 if (typeof(soundManager) === "undefined")
476 {
477 callbackFunctionError("SoundManager 2 not found");
478 return null;
479 }
480
481 //Creates an object and destroy it because otherwise in Intel XDK emulator and Android test with WebView does not work the second time (strange bug):
482 this.soundObject =
483 soundManager.createSound
484 (
485 {
486 id: "fake_sound_sm2_" + CB_AudioFile_API["SM2"]._idUnique,
487 url: filePath
488 }
489 );
490 this.destructor();
491 this.status = CB_AudioFile.LOADING; //The file is loading.
492
493 var onLoadCalled = false;
494 var onLoad =
495 function(success)
496 {
497 onLoadCalled = true;
498 clearTimeout(that._onLoadTimeout);
499
500 //Cancels callbackError call (if any) in case onload has been called before with a wrong "success" (happens on Android with WebView):
501 clearTimeout(that._onErrorFunctionTimeout);
502
503 //if (this.readyState === 3)
504 if (success)
505 {
506 //Prepares the sound to be playable (and plays it automatically if we want to):
507 that._onReadyTimeout = setTimeout(onReadyFunction, 2000); //Gives time to onerror event to be fired before canplaythrough event.
508 }
509 else
510 {
511 //Gives a little bit of time because sometimes the onLoad function is called again (on Android with WebView sometimes the first call contains a wrong "success"):
512 that._onErrorFunctionTimeout =
513 setTimeout
514 (
515 function()
516 {
517 callbackFunctionError("Sound could not be loaded");
518 },
519 1000
520 );
521 }
522 };
523
524 //Creates the object:
525 this.soundObject =
526 soundManager.createSound
527 (
528 {
529 id: this.id + "_" + CB_AudioFile_API["SM2"]._idUnique++,
530 url: filePath,
531 autoLoad: true,
532 onload: onLoad
533 }
534 );
535
536 //Timeout to declare the sound as failed if the onload event is not fired before:
537 this._onLoadTimeout = setTimeout(function() { if (!onLoadCalled) { callbackFunctionError("Sound's onload event timeout (not fired after " + CB_Configuration[CB_BASE_NAME].CB_AudioFile_ONLOAD_TIMEOUT_MS + " ms)"); } }, CB_Configuration[CB_BASE_NAME].CB_AudioFile_ONLOAD_TIMEOUT_MS);
538
539 //Makes the sound seekable for some iOS versions (strange bug) and forces Firefox for Android to fire canplaythrough event (strange bug too):
540 var previousVolume = this.volume; //Stores the current volume.
541 if (CB_Configuration[CB_BASE_NAME].CB_AudioFile_AudioFileCache_MUTE_ON_LOAD_AND_CHECKING)
542 {
543 this.setVolume(0); //Mutes the sound.
544 }
545 this.soundObject.play({ loops: 1 });
546 this._stopAfterPlayingTimeout = setTimeout(function() { that.soundObject.pause(); }, 1);
547
548 //Plays the sound during some time because otherwise some web clients, as WebView used by Cordova on Android, receive duration 0 (strange bug):
549 var onReadyFunctionExecuted = false;
550 var onReadyFunction =
551 function()
552 {
553 //Prevents the execution of the function more than once:
554 if (onReadyFunctionExecuted) { return; }
555 onReadyFunctionExecuted = true;
556
557 //Prevents to be executed after the onerror event has fired:
558 if (that.status === CB_AudioFile.FAILED) { return; }
559
560 //Function to execute when all is OK:
561 var allIsFine =
562 function()
563 {
564 if (that.status === CB_AudioFile.ABORTED) { return; } //If it is has been aborted, we exit.
565
566 if (that._loadingCounterIncreased)
567 {
568 that._loadingCounterIncreased = false;
569 CB_AudioFile_API_AAPI_SM2_beingLoading--; //Decreases the counter of the objects which are loading.
570 if (CB_AudioFile_API_AAPI_SM2_beingLoading &lt; 0) { CB_AudioFile_API_AAPI_SM2_beingLoading = 0; }
571 }
572
573 //Restores the volume:
574 that.setVolume(previousVolume);
575
576 if (typeof(callbackOk) === "function") { callbackOk.call(that); } //Calls the OK function back.
577
578 //Plays automatically if we want to:
579 if (autoPlay) { that.play(); }
580 };
581
582 that.status = CB_AudioFile.UNCHECKED; //The file is still unchecked.
583 //If we want to play automatically, checks if the currentTime changes (some web clients cannot play if the user did not fire an event to call the play function):
584 if (autoPlay)
585 {
586 that.checkPlaying(function() { allIsFine(); }, function(error) { callbackFunctionError(error, true); }, false, false);
587 }
588 else { allIsFine(); }
589 };
590 }
591 catch(E)
592 {
593 callbackFunctionError(E);
594 return null;
595 }
596
597 return this;
598}
599
600
601/**
602 * Checks whether the audio can be played or not. Recommended to be called through a user-driven event (as onClick, onTouch, etc.), as some web clients may need this at least the first time in order to be able to play the audio. Also recommended to use before calling the {@link CB_AudioFile_API.SM2#play} method the first time. The checking action will only be performed if the value of the {@link CB_AudioFile_API.SM2#status} property belongs to the {@link CB_AudioFile.UNCHECKED} or to the {@link CB_AudioFile.CHECKING} value. After checking, if the audio can be played, the {@link CB_AudioFile_API.SM2#status} of the object will get the value of {@link CB_AudioFile.LOADED}. Otherwise, if it cannot be played, the {@link CB_AudioFile_API.SM2#status} property will get the value of {CB_AudioFile.FAILED}.
603 * @function CB_AudioFile_API.SM2#checkPlaying
604 * @param {function} [callbackOk] - Function with no parameters to be called when the audio has been checked successfully, being "this" the {@link CB_AudioFile_API.SM2} object itself.
605 * @param {function} [callbackError] - Function to be called if the audio has not been checked successfully. The first and unique parameter will be a string describing the error found (if it could be determined), being "this" the {@link CB_AudioFile_API.SM2} object itself.
606 * @param {boolean} [ignoreStatus=false] - If set to false and the {@link CB_AudioFile_API.SM2#status} property does not belong neither to the "UNCHECKED" status (defined in the {@link CB_AudioFile.UNCHECKED} constant) nor to the "CHECKING" status (defined in the {@link CB_AudioFile.CHECKING} constant), it will fail calling the "callbackError" function (if any). If set to true, it will try to perform the checking action regardless the status of the audio.
607 * @param {boolean} [ignoreQueue=false] - If set to false and there is already the maximum number of audio files being checked (defined internally), the function will exit and it will call itself automatically again and again until the checking process can be performed (when its queue turn has been reached). This is done for performance purposes.
608 * @param {boolean} [useCache=false] - This parameter will be ignored in this audio API.
609 * @returns {boolean} Returns false if the checking could not be performed and failed. If it returns true, it can mean either the checking has been processed successfully or it will fail in the future, so it is recommended to ignore the returning value and use the callback functions instead.
610 */
611CB_AudioFile_API["SM2"].prototype.checkPlaying = function(callbackOk, callbackError, ignoreStatus, ignoreQueue, useCache)
612{
613 /////clearTimeout(this._recursiveCallCheckingTimeout);
614
615 if (!ignoreStatus &amp;&amp; this.status !== CB_AudioFile.UNCHECKED &amp;&amp; this.status !== CB_AudioFile.CHECKING)
616 {
617 if (typeof(callbackError) === "function") { callbackError.call(this, "Cannot check if status is not unchecked or checking (status is " + this.status + ")"); }
618 return false;
619 }
620
621 this.status = CB_AudioFile.CHECKING;
622
623 var that = this;
624
625 //If we do not use cache the maximum of objects checking is reached or we use cache but the file path is checking, calls the function again after some time and exits:
626 if (!ignoreQueue &amp;&amp; CB_AudioFile_API_AAPI_SM2_beingChecking >= CB_AudioFile_API_AAPI_SM2_maximumChecking)
627 {
628 this._recursiveCallCheckingTimeout = setTimeout(function() { that.checkPlaying(callbackOk, callbackError, ignoreStatus, useCache); }, 10);
629 return true;
630 }
631
632 ////this.status = CB_AudioFile.CHECKING;
633
634 if (!this._checkingCounterIncreased)
635 {
636 this._checkingCounterIncreased = true;
637 CB_AudioFile_API_AAPI_SM2_beingChecking++; //Increases the counter of the objects which are checking.
638 }
639
640 var previousVolume = this.volume;
641 var finishedChecking =
642 function(ok, error, keepStatus)
643 {
644 //Stops the file:
645 that.soundObject.pause();
646
647 //Restores the volume:
648 that._checkPlayingFinishingTimeout = //Timeout to prevent hearing the sound in some web clients.
649 setTimeout
650 (
651 function()
652 {
653 that.setVolume(previousVolume);
654
655 //If the file is ok:
656 if (ok)
657 {
658 if (!keepStatus) { that.status = CB_AudioFile.LOADED; }
659 if (typeof(callbackOk) === "function") { callbackOk.call(that); }
660 }
661 //...otherwise, if the file has failed:
662 else
663 {
664 if (!keepStatus) { that.status = CB_AudioFile.FAILED; }
665 if (typeof(callbackError) === "function") { callbackError.call(that, error); }
666 }
667 },
668 10
669 );
670
671 if (that._checkingCounterIncreased)
672 {
673 that._checkingCounterIncreased = false;
674 CB_AudioFile_API_AAPI_SM2_beingChecking--; //Decreases the counter of the objects which are checking.
675 if (CB_AudioFile_API_AAPI_SM2_beingChecking &lt; 0) { CB_AudioFile_API_AAPI_SM2_beingChecking = 0; }
676 }
677 };
678
679
680 try
681 {
682 //Plays the sound during some time to let some web clients get the duration correctly (strange bug):
683 if (CB_Configuration[CB_BASE_NAME].CB_AudioFile_AudioFileCache_MUTE_ON_LOAD_AND_CHECKING)
684 {
685 this.setVolume(0);
686 }
687 this.soundObject.play({ loops: 1 });
688
689 var durationDetected = this.getDuration();
690 var timesCurrentTimeChecked = 0;
691 clearTimeout(this._checkPlayingTimeout);
692 clearTimeout(this._checkPlayingFinishingTimeout);
693
694 var checkFunction =
695 function(callbackOk, callbackError)
696 {
697 clearTimeout(that._checkPlayingTimeout);
698
699 //If it is has been aborted, we exit:
700 if (that.status === CB_AudioFile.ABORTED || that.status === CB_AudioFile.FAILED)
701 {
702 finishedChecking(false, "Audio file object is " + (that.status === CB_AudioFile.ABORTED ? "ABORTED" : "FAILED") + ".", true);
703 return;
704 }
705
706 try
707 {
708 that.status = CB_AudioFile.CHECKING;
709
710 //If the duration has changed, it calls the function again because it means the audio is still loading (otherwise, Firefox has issues with data URIs and detects a shorter duration):
711 var durationDetectedNow = that.getDuration();
712 if (durationDetected !== durationDetectedNow)
713 {
714 durationDetected = durationDetectedNow;
715 that._checkPlayingTimeout = setTimeout(function() { checkFunction(callbackOk, callbackError); }, CB_Configuration[CB_BASE_NAME].CB_AudioFile_AUTOPLAY_SILENTLY_ON_LOAD_MS);
716 return;
717 }
718
719 //If the current time is still 0:
720 if (that.soundObject.position == 0)
721 {
722 //We give it some opportunities more to change current time:
723 if (timesCurrentTimeChecked &lt; 1000)
724 {
725 timesCurrentTimeChecked++;
726 if (timesCurrentTimeChecked % 20 === 0)
727 {
728 that.soundObject.pause();
729 try { that.soundObject.setPosition(0); } catch(E) {} //Avoid Firefox complain about using an object which is no longer available.
730 that.soundObject.play({ loops: 1 });
731 /*
732 try
733 {
734 if (that.soundObject.position != 0)
735 {
736 that.soundObject.setPosition(0.00000000001); //Executed after play method because otherwise Safari Mobile does not change currentTime.
737 }
738 } catch(E) {}
739 */
740 }
741 that._checkPlayingTimeout = setTimeout(function() { checkFunction(callbackOk, callbackError); }, 1);
742 return;
743 }
744 //...if all opportunities failed, we declare it as FAILED and exits:
745 else
746 {
747 finishedChecking(false, "position does not change (it is " + that.soundObject.position + ").");
748 return;
749 }
750 }
751
752 //Only updates the currentTime to 0 if it is not zero already (because otherwise some web clients will fail or have a wrong behaviour):
753 if (that.soundObject.position != 0) { that.soundObject.setPosition(0); }
754
755 //If the duration is zero, we declare it as FAILED and exits:
756 if (that.getDuration() === 0)
757 {
758 finishedChecking(false, "Duration is zero");
759 return;
760 }
761 else if (that.soundObject.readyState === 2)
762 {
763 finishedChecking(false, "readyState is 2 (failed/error)");
764 return;
765 }
766
767 finishedChecking(true);
768 }
769 catch(E)
770 {
771 finishedChecking(false, E);
772 }
773 };
774 this._checkPlayingTimeout = setTimeout(function() { checkFunction(callbackOk, callbackError); }, CB_Configuration[CB_BASE_NAME].CB_AudioFile_AUTOPLAY_SILENTLY_ON_LOAD_MS);
775 return true;
776 }
777 catch(E)
778 {
779 finishedChecking(false, E);
780 return false;
781 }
782}
783
784
785/**
786 * Tells the duration of the audio (in milliseconds). Note that some clients might not calculate the duration correctly and, in this case, a zero (0) value would be returned.
787 * @function CB_AudioFile_API.SM2#getDuration
788 * @returns {number} Returns the duration of the audio (in milliseconds). Note that some clients might not calculate the duration correctly and, in this case, a zero (0) value would be returned.
789 */
790CB_AudioFile_API["SM2"].prototype.getDuration = function()
791{
792 var duration;
793
794 if (typeof(this.soundObject) !== "undefined" &amp;&amp; this.soundObject !== null &amp;&amp; typeof(this.soundObject.duration) !== "undefined" &amp;&amp; this.soundObject.duration !== null)
795 {
796 duration = this.soundObject.duration;
797 }
798
799 if (typeof(duration) === "undefined" || duration === null || isNaN(duration) || duration &lt; 0) { duration = 0; }
800
801 return duration;
802}
803
804
805//Executed when there is an error playing:
806CB_AudioFile_API["SM2"].prototype._onPlayingErrorFunction = function()
807{
808 this._onPlayingErrorFunctionExecuting = true;
809 clearTimeout(this._onPlayingErrorFunctionTimeout);
810 if (this.soundObject.readyState === 2 &amp;&amp; this.status === CB_AudioFile.LOADED)
811 {
812 this.status = CB_AudioFile.FAILED; //Declares it as failed.
813 this.stop(false, false, true); //Also fires onStop event.
814 }
815 if (this.status === CB_AudioFile.LOADED)
816 {
817 var that = this;
818 this._onPlayingErrorFunctionTimeout = setTimeout(function() { that._onPlayingErrorFunction.call(that); }, 1);
819 }
820 else { this._onPlayingErrorFunctionExecuting = false; }
821};
822
823
824//Executed when the sound stops playing:
825CB_AudioFile_API["SM2"].prototype._whenStopFunction = function(startAtNextLoop, stopAt, avoidDelayedPlay, allowedRecursiveDelay, onPlayStart, onLoadError, whenStopFunction, onPlayingErrorFunction, flags)
826{
827 //Just allows executing the function once:
828 if (flags.whenStopFunctionExecuted) { return; }
829 flags.whenStopFunctionExecuted = true;
830
831 //Removes the event and timeout:
832 this.soundObject.clearOnPosition(stopAt, whenStopFunction); //Clears the event:
833 clearTimeout(this._timeoutWhenStop); //Clears the previous timeout.
834
835 //If the sound has been stopped or paused or the stop time has changed, exits:
836 if (this.stopped || this.paused || this.lastStopAt !== stopAt) { return; }
837 //...otherwise, if the stop time has not been reached yet, calls the function again after a while:
838 //else if (this.getCurrentTime() &lt; stopAt) { setTimeout(whenStopFunction, 1); return; }
839
840 //If the object has failed, just calls the error function and exits:
841 if (this.soundObject.readyState === 2)
842 {
843 clearTimeout(this._onPlayingErrorFunctionTimeout);
844 this._onPlayingErrorFunctionTimeout = setTimeout(onPlayingErrorFunction, 1);
845 return;
846 }
847
848 //If we want to loop, loops again:
849 if (this.loop)
850 {
851 //this.stop(true); //Stops the sound without setting its property as stopped.
852 this.play(startAtNextLoop, stopAt, true, avoidDelayedPlay, allowedRecursiveDelay, onPlayStart, onLoadError, false, true); //Plays again the sound.
853 }
854 //...otherwise, if we don't want to loop, we stop:
855 else { this.stop(); }
856};
857
858
859/**
860 * Plays the audio.
861 * @function CB_AudioFile_API.SM2#play
862 * @param {number} [startAt=0 | {@link CB_AudioFile_API.SM2#lastStartAt} | stopAt] - Time in milliseconds where we want the audio to start at. If not provided or it is not a valid number, it will use zero (0) as default which belongs to the beginning of the audio. If the value provided is greater than the "stopAt" provided, it will use the value set in the {@link CB_AudioFile_API.SM2#lastStartAt} property (which belongs to the "startAt" value the last time that this method was called). If, even using the {@link CB_AudioFile_API.SM2#lastStartAt} value is still greather than the "stopAt" provided, it will use the same value as the "stopAt" which means it will not play and will stop immediately.
863 * @param {number} [stopAt={@link CB_AudioFile_API.SM2#getDuration}()] - Time in milliseconds where we want the audio to stop at. If not provided or it is not a valid number, it will use the returning value of the {@link CB_AudioFile_API.SM2#getDuration} method (which should belong to the total duration of the audio, if it was calculated correctly).
864 * @param {boolean} [loop={@link CB_AudioFile_API.SM2#loop}] - Sets whether we want to play the audio looping (starting again and again) or just play it once. Note that at the end of each loop the "onStop" function defined (through the {@link CB_AudioFile_API.SM2#onStop} method) will not be called.
865 * @param {boolean} [avoidDelayedPlay=false] - If set to false (recommended) and the audio failed previously or was aborted (destroyed), it will try to load it correctly again automatically and play it after that if possible (this can take some time so the audio could start playing after a delay). Otherwise, if set to true and the audio failed or was aborted (destroyed), the audio will not play at all and the "stop" method will be called immediately.
866 * @param {boolean} [allowedRecursiveDelay={@link CB_Configuration.CrossBase.CB_AudioFile_AudioFileCache_ALLOWED_RECURSIVE_DELAY_DEFAULT}] - The maximum amount of time (in milliseconds) of delay that we accept before start playing the audio. If the amount of time is overcome, the audio will not play at all and the {@link CB_AudioFile_API.SM2#stop} method will be called immediately. Used only when the "avoidDelayedPlay" parameter is set to false and the audio needs to be loaded because it failed previously or was aborted (destroyed).
867 * @param {function} [onPlayStart] - Function to be called when the audio starts playing successfully. The function will be called with the following parameters (in order): "startAt", "stopAt", "startAtNextLoop", "loop", "avoidDelayedPlay", "allowedRecursiveDelay" and "startPlayingTime", being "this" the {@link CB_AudioFile_API.SM2} object. If the audio is looping, this will be called only once when the audio starts playing the first time and it will not be called next loops.
868 * @param {function} [onLoadError] - Function to be called if the audio cannot be played successfully. The first and unique parameter will be a string describing the error found (if it could be determined), being "this" the {@link CB_AudioFile_API.SM2} object.
869 * @param {boolean} [isResume=false] - If set to true (not recommended) and it is a looping audio, the next loop will use the value of the {@link CB_AudioFile_API.SM2#lastStartAt} property as the "startAt" parameter when it calls this method again automatically (internally). Recommended for internal usage only.
870 * @param {boolean} [isLooping=false] - Used to determine whether this method was called automatically again by itself because it is looping the audio. Recommended for internal usage only.
871 * @param {integer} [startPlayingTime] - Contains the time when the audio should start playing. Recommended for internal usage only.
872 * @returns {boolean|integer} It returns false if the duration is 0 ("startAt" and "stopAt" are the same number), returns "-1" if the audio cannot be played and an error is detected or returns true otherwise. Note that even when it returns true there can be a non-detectable error and maybe the audio is not played.
873 */
874CB_AudioFile_API["SM2"].prototype.play = function(startAt, stopAt, loop, avoidDelayedPlay, allowedRecursiveDelay, onPlayStart, onLoadError, isResume, isLooping, startPlayingTime)
875{
876 var that = this;
877
878 var duration = this.getDuration();
879
880 if (typeof(startPlayingTime) === "undefined" || startPlayingTime === null) { startPlayingTime = CB_Device.getTiming(); }
881
882 var onPlayingErrorFunction = function() { that._onPlayingErrorFunction.call(that); };
883
884 //If the sound is not ready yet, calls the function again but later:
885 /*if (this.status !== CB_AudioFile.LOADED || this.getDuration() === 0) //Duration must be greater than zero.
886 {
887 this.stopped = true;
888 this.paused = false;
889 //If it has not failed or aborted, calls the function again but later:
890 if (this.status !== CB_AudioFile.FAILED &amp;&amp; this.status !== CB_AudioFile.ABORTED) { setTimeout(function() { that.play(startAt, stopAt, loop, allowOverlapping, avoidDelayedPlay, onLoadError, isResume, isLooping); }, 1); }
891 //...otherwise, if it has failed, sets it as stopped:
892 //else { this.stopped = true; }
893 return -1;
894 }*/
895 if (this.status !== CB_AudioFile.LOADED || duration === 0 || this.soundObject.readyState === 2) //Duration must be greater than zero.
896 {
897 this.stopped = true;
898 this.paused = false;
899
900 //If it has not failed or aborted:
901 if (this.status !== CB_AudioFile.FAILED &amp;&amp; this.status !== CB_AudioFile.ABORTED) //It must be UNLOADED, LOADING, LOADED, UNCHECKED or CHECKING.
902 {
903 //Function that calls the play method recursively (unless the maximum time allowed has expired):
904 var playLaterFunctionCalled = false;
905 var playLaterFunction =
906 function()
907 {
908 if (playLaterFunctionCalled) { return; }
909 playLaterFunctionCalled = true;
910
911 //If the recursive delay is not null and is a valid number:
912 if (typeof(allowedRecursiveDelay) === "undefined" || allowedRecursiveDelay === null || isNaN(allowedRecursiveDelay) || allowedRecursiveDelay &lt; 0)
913 {
914 allowedRecursiveDelay = CB_Configuration[CB_BASE_NAME].CB_AudioFile_AudioFileCache_ALLOWED_RECURSIVE_DELAY_DEFAULT; //We use default value.
915 }
916 var timeNow = CB_Device.getTiming();
917
918 //If the time expired is less or equal to the delay allowed:
919 if (timeNow - startPlayingTime &lt;= allowedRecursiveDelay)
920 {
921 //Calls play method again:
922 that.play(startAt, stopAt, loop, avoidDelayedPlay, allowedRecursiveDelay, onPlayStart, onLoadError, isResume, isLooping, startPlayingTime);
923 }
924 //...otherwise, just stops the sound:
925 ///////else { that.stop(false, false); } //Sets as stopped and fires onStop function (if any).
926 else { that.stop(false, false, true); } //Sets as stopped and fires onStop function (if any).
927 };
928
929 //Function to execute when the sound loads successfully (or finishes checking successfully):
930 var onLoad =
931 function()
932 {
933 //If we allow delayed play, plays the sound:
934 if (!avoidDelayedPlay) { playLaterFunction(); }
935 //...otherwise, just stops the sound (to fire onStop function):
936 ////////else { that.stop(false, false); } //Sets as stopped and fires onStop function (if any).
937 else { that.stop(false, false, true); } //Sets as stopped and fires onStop function (if any).
938 };
939
940 //If it is UNLOADED or we had a duration before but not now and it is not LOADING, loads the file again:
941 if (this.status === CB_AudioFile.UNLOADED || this.status !== CB_AudioFile.LOADING &amp;&amp; this._lastDuration !== null &amp;&amp; duration === 0 || this.soundObject.readyState === 2)
942 {
943 this.load(this.filePath, false, onLoad, onLoadError, true);
944 }
945 //...otherwise, if it is UNCHECKED, we call the checking function:
946 else if (this.status === CB_AudioFile.UNCHECKED)
947 {
948 this.checkPlaying(onLoad, onLoadError, false, true, false);
949 }
950 //...otherwise, if it is not CHECKING (it must be LOADING or LOADED with duration 0 from the beginning), we will not reload the sound:
951 else if (this.status !== CB_AudioFile.CHECKING)
952 {
953 //If we allow delayed play, calls the play method again but after some time:
954 if (!avoidDelayedPlay) { setTimeout(playLaterFunction, 1); }
955 //...otherwise, just stops the sound (to fire onStop function):
956 //////else { this.stop(false, false); } //Sets as stopped and fires onStop function (if any).
957 else { this.stop(false, false, true); } //Sets as stopped and fires onStop function (if any).
958 }
959 }
960 return -1;
961 }
962
963 this._lastDuration = duration;
964
965 //Defines the default parameters:
966 if (CB_trim(startAt) === "") { startAt = 0; } //Starts at the beginning as default.
967 if (CB_trim(stopAt) === "") { stopAt = 0; } //If it is not a number, default is zero.
968 if (typeof(loop) === "undefined" || loop === null) { loop = this.loop; } //If not set, uses the default (or last one used).
969 else { this.loop = loop; } //...otherwise, stores the new setting given.
970
971 //Sanitizes startAt and stopAt:
972 startAt = parseFloat(startAt);
973 stopAt = parseFloat(stopAt);
974 if (startAt &lt; 0) { startAt = 0; }
975 if (stopAt &lt;= 0 || stopAt > duration) { stopAt = duration; } //If the stopAt is not correct, plays until the end of the file.
976 if (startAt > stopAt) { startAt = this.lastStartAt; } //In the case start time is than stop time, starts as the previous time.
977
978 if (startAt > stopAt || isNaN(startAt)) { startAt = stopAt; }
979
980 //If the duration is zero (startAt and stopAt are equal), exits:
981 if (startAt === stopAt) { this.stop(); return false; }
982
983 //Next loop (if any) it will start at the same time by default:
984 var startAtNextLoop = startAt;
985 //If it is a resume, next loop we should start from the previous startAt used:
986 if (isResume) { startAtNextLoop = this.lastStartAt; }
987 //...otherwise, if it is not a resume, stores the startAt used:
988 else { this.lastStartAt = startAt; }
989 this.lastStopAt = stopAt; //Stores stopAt used.
990
991 //Adds the event to check when the file reaches the stop time:
992 var whenStopFunction = function() { that._whenStopFunction.call(that, startAtNextLoop, stopAt, avoidDelayedPlay, allowedRecursiveDelay, onPlayStart, onLoadError, whenStopFunction, onPlayingErrorFunction, flags); };
993 var flags = { "whenStopFunctionExecuted" : false };
994
995 //Clears the previous timeout (if any):
996 clearTimeout(this._timeoutWhenStop);
997
998 //If it is looping or does not allow overlapping and it is not paused, stops the possible previous sound:
999 //if (isLooping || !allowOverlapping &amp;&amp; !this.paused) { this.stop(true); } //Stops the sound without setting its property as stopped.
1000 this.stop(true, true); //Stops the sound without setting its property as stopped.
1001
1002 //Applies the current volume:
1003 this.setVolume(this.volume);
1004
1005 //If it is not a loop (it is the first call to the play method) or we do not want to loop:
1006 if (!isLooping || !loop) { CB_symmetricCallClear("SM2_AUDIO_FILE" + this._id_internal); } //We clean the cache of setTimeoutSynchronized for the loop function.
1007
1008
1009 //If defined, starts at the given position (ms):
1010 var currentTime = startAt;
1011 //Only updates the currentTime to 0 if it is not zero already (because otherwise some web clients will fail or have a wrong behaviour):
1012 if (currentTime !== 0 || this.soundObject.position != 0)
1013 {
1014 this.soundObject.setPosition(currentTime);
1015 }
1016 //this.soundObject.setPosition(startAt);
1017
1018 //Plays the file:
1019 this.soundObject.play({ loops: 99999999, onfinish: whenStopFunction }); //We will use our own way to loop. Many loops (although seems that 2 would be enough) because Android WebView returns position as 0 the second loop if the file is played completely.
1020
1021 //If defined, starts at the given position (ms):
1022 //Only updates the currentTime to 0 if it is not zero already (because otherwise some web clients will fail or have a wrong behaviour):
1023 if (currentTime !== 0 || this.soundObject.position != 0)
1024 {
1025 this.soundObject.setPosition(currentTime + 0.00000000001);
1026 }
1027 //this.soundObject.setPosition(startAt); //Executed after play method because otherwise Intel XDK emulator and Android WebView kit does not perform setPosition.
1028
1029 //Sets the event and timeout for when the sound finishes:
1030 this.soundObject.onPosition(stopAt, whenStopFunction);
1031 var msToFinish = stopAt - startAt;
1032 this._timeoutWhenStop = CB_symmetricCall(whenStopFunction, msToFinish, "SM2_AUDIO_FILE" + this._id_internal);
1033
1034 //The sound is neither paused nor stopped:
1035 this._startPlayingTimeReal = CB_Device.getTiming(); //Useful to calculate approximate currentTime when position returns 0 (SM2 bug).
1036 this.paused = this.stopped = false;
1037
1038 //If it is has failed, calls the onPlayingError function:
1039 if (!this._onPlayingErrorFunctionExecuting)
1040 {
1041 clearTimeout(this._onPlayingErrorFunctionTimeout);
1042 this._onPlayingErrorFunctionTimeout = setTimeout(onPlayingErrorFunction, 1);
1043 }
1044 if (this.soundObject.readyState === 2) { return -1; }
1045
1046 //If it is the first time (not a loop) and there is a function to call when the play starts, we call it:
1047 if (!isLooping &amp;&amp; typeof(onPlayStart) === "function") { onPlayStart.call(this, startAt, stopAt, startAtNextLoop, loop, avoidDelayedPlay, allowedRecursiveDelay, startPlayingTime); onPlayStart = null; } //Prevents execution again.
1048
1049 return true;
1050}
1051
1052
1053/**
1054 * Resumes the audio (after being paused), starting from the same point it was paused previously.
1055 * @function CB_AudioFile_API.SM2#resume
1056 * @param {boolean} [loop={@link CB_AudioFile_API.SM2#loop}] - Sets whether we want to play the audio looping (starting again and again) or just play it once. Note that at the end of each loop the "onStop" function defined (through the {@link CB_AudioFile_API.SM2#onStop} method) will not be called.
1057 * @param {boolean} [avoidDelayedPlay=false] - If set to false (recommended) and the audio failed previously or was aborted (destroyed), it will try to load it correctly again automatically and play it after that if possible (this can take some time so the audio could start playing after a delay). Otherwise, if set to true and the audio failed or was aborted (destroyed), the audio will not play at all and the {@link CB_AudioFile_API.SM2#stop} method will be called immediately.
1058 * @param {boolean} [allowedRecursiveDelay={@link CB_Configuration.CrossBase.CB_AudioFile_AudioFileCache_ALLOWED_RECURSIVE_DELAY_DEFAULT}] - The maximum amount of time (in milliseconds) of delay that we accept before start playing the audio. If the amount of time is overcome, the audio will not play at all and the {@link CB_AudioFile_API.SM2#stop} method will be called immediately. Used only when the "avoidDelayedPlay" parameter is set to false and the audio needs to be loaded because it failed previously or was aborted (destroyed).
1059 * @param {function} [onPlayStart] - Function to be called when the audio starts playing successfully. The function will be called with the following parameters (in order): "startAt", "stopAt", "startAtNextLoop", "loop", "avoidDelayedPlay", "allowedRecursiveDelay" and "startPlayingTime", being "this" the {@link CB_AudioFile_API.SM2} object. If the audio is looping, this will be called only once when the audio starts playing the first time and it will not be called next loops.
1060 * @param {function} [onLoadError] - Function to be called if the audio cannot be played successfully. It will not be called if the audio is not paused or is stopped. The first and unique parameter will be a string describing the error found (if it could be determined), being "this" the {@link CB_AudioFile_API.SM2} object.
1061 * @returns {boolean|integer} Returns the returning value of the {@link CB_AudioFile_API.SM2#play} method which is called internally. It returns false if the audio is not paused or it is stopped, returns "-1" if the audio cannot be played and an error is detected or returns true otherwise. Note that even when it returns true there can be a non-detectable error and maybe the audio is not played.
1062 */
1063CB_AudioFile_API["SM2"].prototype.resume = function(loop, avoidDelayedPlay, allowedRecursiveDelay, onPlayStart, onLoadError)
1064{
1065 //If it not paused or it is stopped, exits the function:
1066 if (!this.paused || this.stopped) { return false; }
1067
1068 //this._resuming = true;
1069
1070 var startAt = this.pauseTime;
1071
1072 //If it has been paused after the stop time (happens sometimes when the sound was nearly to finish):
1073 if (startAt >= this.lastStopAt)
1074 {
1075 startAt = this.lastStopAt - 1; //We will begin just 1 millisecond before (otherwise the play method would begin again from lastStartAt).
1076 }
1077
1078 return this.play(startAt, this.lastStopAt, loop, avoidDelayedPlay, allowedRecursiveDelay, onPlayStart, onLoadError, true, false);
1079}
1080
1081
1082/**
1083 * Pauses the audio when it is being played.
1084 * @function CB_AudioFile_API.SM2#pause
1085 * @param {function} [onPause] - Function without parameters to be called when the audio is paused successfully, being "this" the {@link CB_AudioFile_API.SM2} object.
1086 * @param {boolean} [keepPausedUnaltered=false] - If set to true (not recommended), the {@link CB_AudioFile_API.SM2#paused} property will not be set to true and it will remain with its current value.
1087 * @returns {boolean} It returns false if the audio is already paused or it is stopped or if it cannot be paused. Returns true otherwise.
1088 */
1089CB_AudioFile_API["SM2"].prototype.pause = function(onPause, keepPausedUnaltered)
1090{
1091 //If it already paused or stopped, exits the function:
1092 if (this.paused || this.stopped) { return false; }
1093
1094 //this._resuming = false;
1095
1096 if (typeof(this.soundObject) !== "undefined" &amp;&amp; this.soundObject !== null)
1097 {
1098 clearTimeout(this._timeoutWhenStop); //Prevents that the file is set as stopped or to be executed again.
1099 this.soundObject.pause();
1100 this.pauseTime = this.getCurrentTime();
1101
1102 //Bug fix: sometimes SM2 returns position as 0 (getCurrentTime returns 0) and pauseTime is 0.
1103 if (this.pauseTime === 0)
1104 {
1105 var timeNow = CB_Device.getTiming();
1106 var pauseTime = this.lastStartAt + (timeNow - this._startPlayingTimeReal);
1107 if (pauseTime > this.lastStopAt)
1108 {
1109 pauseTime = this.lastStartAt;
1110 }
1111 this.pauseTime = pauseTime;
1112 }
1113
1114 if (!keepPausedUnaltered) { this.paused = true; }
1115 if (typeof(onPause) === "function") { onPause.call(this); }
1116 return true;
1117 }
1118
1119 return false;
1120}
1121
1122
1123/**
1124 * Stops the audio.
1125 * @function CB_AudioFile_API.SM2#stop
1126 * @param {boolean} [keepStoppedUnaltered=false] - If set to true (not recommended), the {@link CB_AudioFile_API.SM2#stopped} property will not be set to true and it will remain with its current value.
1127 * @param {boolean} [avoidOnStop=false] - If set to false and there is an "onStop" function defined (through the {@link CB_AudioFile_API.SM2#onStop} method), it will be called after stopping the audio (or after trying to do it, at least) but only if either the "forceOnStop" parameter is set to true or if the "keepStoppedUnaltered" parameter is set to false and the audio was not already stopped before. If set to true, the "onStop" function (if any) will not be called at all.
1128 * @param {boolean} [forceOnStop=false] - If it is set to true and the "avoidOnStop" parameter is set to false and there is an "onStop" function defined (through the {@link CB_AudioFile_API.SM2#onStop} method), it will be called regardless the audio was stopped before or not. If set to false, the "onStop" function (if any) will only be called if the "keepStoppedUnaltered" parameter is set to false and the audio was not already stopped before. This parameter will be ignored if the "avoidOnStop" parameter is set to true.
1129 * @returns {boolean} It returns false if the stopping action cannot be performed at all (this could happen when the audio has not been loaded properly, for example). Returns true otherwise (this only means that it has been tried to be stopped but it could not be successfully).
1130 */
1131CB_AudioFile_API["SM2"].prototype.stop = function(keepStoppedUnaltered, avoidOnStop, forceOnStop)
1132{
1133 if (typeof(this.soundObject) !== "undefined" &amp;&amp; this.soundObject !== null)
1134 {
1135 ///////clearTimeout(this._timeoutWhenStop); //Prevents that the file is set as stopped or to be executed again.
1136 this.soundObject.stop();
1137 //Only updates the currentTime to 0 if it is not zero already (because otherwise some web clients will fail or have a wrong behaviour):
1138 if (this.soundObject.position != 0)
1139 {
1140 this.soundObject.setPosition(0);
1141 }
1142 var stoppedBefore = this.stopped;
1143 if (!keepStoppedUnaltered) { this.stopped = true; }
1144 this.paused = false; //If it is stopped, it is not paused.
1145 //If we do not want to avoid onStop, it was not stopped before but it is now and onStop has a valid function assigned, we execute it:
1146 if (!avoidOnStop &amp;&amp; (!stoppedBefore &amp;&amp; this.stopped || forceOnStop) &amp;&amp; typeof(this.onStopFunction) === "function") { this.onStopFunction.call(this); }
1147 return true;
1148 }
1149 return false;
1150}
1151
1152
1153/**
1154 * Sets the desired volume for the audio file (from 0 to the maximum value, where the maximum value will be the returning value of calling the {@link CB_Speaker.getVolume} function if the {@link CB_Configuration.CrossBase.CB_AudioFile_AudioFileCache_USE_SPEAKER_VOLUME_AS_MAXIMUM} property is set to true or it will be 100 otherwise).
1155 * @function CB_AudioFile_API.SM2#setVolume
1156 * @param {number} [volume={@link CB_Speaker.getVolume()} | {@link CB_Configuration.CrossBase.CB_Speaker_DEFAULT_VOLUME}] - Desired volume (from 0 to the maximum value, where the maximum value will be the returning value of calling the {@link CB_Speaker.getVolume} function if the {@link CB_Configuration.CrossBase.CB_AudioFile_AudioFileCache_USE_SPEAKER_VOLUME_AS_MAXIMUM} property is set to true or it will be 100 otherwise).
1157 * @param {boolean} [forceSetVolumeProperty=false] - If set to true (not recommended), it will change the {@link CB_AudioFile_API.SM2#volume} property even when the volume failed to be changed.
1158 * @param {function} [onSetVolume] - Callback function which will be called if it has been possible to set the volume (or at least it was possible to try it). It will not receive any parameters, being "this" the {@link CB_AudioFile_API.SM2} object.
1159 * @param {boolean} [saveForUnmute=false] - If set to true (not recommended), it will save internally the current volume before setting it so it will restore the same volume again after calling the {@link CB_AudioFile_API.SM2#unmute} method. Internal usage only recommended.
1160 * @returns {number} Returns the current volume (from 0 to the maximum value, where the maximum value will be the returning value of calling the {@link CB_Speaker.getVolume} function if the {@link CB_Configuration.CrossBase.CB_AudioFile_AudioFileCache_USE_SPEAKER_VOLUME_AS_MAXIMUM} property is set to true or it will be 100 otherwise).
1161 */
1162CB_AudioFile_API["SM2"].prototype.setVolume = function(volume, forceSetVolumeProperty, onSetVolume, saveForUnmute)
1163{
1164 //Defines the default parameters:
1165 volume = parseInt(volume);
1166 if (isNaN(volume))
1167 {
1168 this.DEFAULT_VOLUME = CB_Configuration[CB_BASE_NAME].CB_AudioFile_AudioFileCache_USE_SPEAKER_VOLUME_AS_DEFAULT ? CB_Speaker.getVolume() : CB_Configuration[CB_BASE_NAME].CB_Speaker_DEFAULT_VOLUME;
1169 volume = this.DEFAULT_VOLUME;
1170 }
1171
1172 //Sets the volume within their limits if it is beyond them:
1173 var MAX_VOLUME = CB_Configuration[CB_BASE_NAME].CB_AudioFile_AudioFileCache_USE_SPEAKER_VOLUME_AS_MAXIMUM ? CB_Speaker.getVolume() : 100;
1174 if (volume > MAX_VOLUME) { volume = MAX_VOLUME; }
1175 else if (volume &lt; 0) { volume = 0; }
1176
1177 if (typeof(this.soundObject) !== "undefined" &amp;&amp; this.soundObject !== null)
1178 {
1179 this.soundObject.setVolume(volume);
1180 if ((saveForUnmute || volume === 0) &amp;&amp; this.volume > 0) { this.volumeBeforeMute = this.volume; } //Also saves the previous volume if the desired one is zero (muted).
1181 this.volume = volume;
1182 if (typeof(onSetVolume) === "function") { onSetVolume.call(this); }
1183 }
1184
1185 if (forceSetVolumeProperty) { this.volume = volume; }
1186
1187 return this.volume;
1188}
1189
1190
1191/**
1192 * Mutes the audio file.
1193 * @function CB_AudioFile_API.SM2#mute
1194 * @param {function} [onMute] - Callback function which will be called if it has been possible to mute the audio file (or at least it was possible to try it).
1195 * @returns {number} Returns the current volume (from 0 to the maximum value, where the maximum value will be the returning value of calling the {@link CB_Speaker.getVolume} function if the {@link CB_Configuration.CrossBase.CB_AudioFile_AudioFileCache_USE_SPEAKER_VOLUME_AS_MAXIMUM} property is set to true or it will be 100 otherwise). If all goes well, the returning value should be zero (0). Note that, even when it returns a zero (0) value, this does not always mean that the mute has been applied successfully.
1196 */
1197CB_AudioFile_API["SM2"].prototype.mute = function(onMute)
1198{
1199 //Only mutes the sound if it is not muted already:
1200 if (this.volume > 0)
1201 {
1202 //Mutes the audio:
1203 this.setVolume(0, false, onMute, true); //It modifies this.volumeBeforeMute.
1204 }
1205 return this.volume;
1206}
1207
1208
1209/**
1210 * Restores audio after muting it (unmutes it).
1211 * @function CB_AudioFile_API.SM2#unmute
1212 * @param {function} [onUnmute] - Callback function which will be called if it has been possible to unmute the audio file (or at least it was possible to try it). It will not receive any parameters, being "this" the {@link CB_AudioFile_API.SM2} object.
1213 * @returns {number} Returns the current volume (from 0 to the maximum value, where the maximum value will be the returning value of calling the {@link CB_Speaker.getVolume} function if the {@link CB_Configuration.CrossBase.CB_AudioFile_AudioFileCache_USE_SPEAKER_VOLUME_AS_MAXIMUM} property is set to true or it will be 100 otherwise).
1214 */
1215CB_AudioFile_API["SM2"].prototype.unmute = function(onUnmute)
1216{
1217 //Only unmutes if it is still muted:
1218 if (this.volume === 0)
1219 {
1220 //Restores the volume before muting:
1221 this.setVolume(this.volumeBeforeMute, false, onUnmute);
1222 }
1223 return this.volume;
1224}
1225
1226
1227/**
1228 * Gets the current time (in milliseconds) which belongs to the position where the audio is currently playing or where it has been paused. Note that some audio APIs and clients could give wrong values.
1229 * @function CB_AudioFile_API.SM2#getCurrentTime
1230 * @returns {number} Returns the current time (in milliseconds). Note that some audio APIs and clients could give wrong values.
1231 */
1232CB_AudioFile_API["SM2"].prototype.getCurrentTime = function()
1233{
1234 var currentTime;
1235
1236 if (this.status !== CB_AudioFile.LOADED) { return 0; }
1237
1238 if (typeof(this.soundObject) !== "undefined" &amp;&amp; this.soundObject !== null)
1239 {
1240 currentTime = this.soundObject.position;
1241 }
1242
1243 if (typeof(currentTime) === "undefined" || currentTime === null || isNaN(currentTime) || currentTime &lt; 0) { currentTime = 0; }
1244
1245 return currentTime;
1246}
1247
1248
1249/*
1250//Returns the bytes loaded of the file:
1251CB_AudioFile_API["SM2"].prototype.getBytesLoaded = function()
1252{
1253 var bytesLoaded;
1254
1255 if (typeof(this.soundObject) !== "undefined" &amp;&amp; this.soundObject !== null)
1256 {
1257 bytesLoaded = this.soundObject.bytesLoaded;
1258 }
1259
1260 if (typeof(bytesLoaded) === "undefined" || bytesLoaded === null || isNaN(bytesLoaded)) { bytesLoaded = 0; }
1261
1262 return bytesLoaded;
1263}
1264
1265
1266//Returns the total bytes of the file:
1267CB_AudioFile_API["SM2"].prototype.getBytesTotal = function()
1268{
1269 var bytesTotal;
1270
1271 if (typeof(this.soundObject) !== "undefined" &amp;&amp; this.soundObject !== null)
1272 {
1273 bytesTotal = this.soundObject.bytesTotal;
1274 }
1275
1276 if (typeof(bytesTotal) === "undefined" || bytesTotal === null || isNaN(bytesTotal)) { bytesTotal = 0; }
1277
1278 return bytesTotal;
1279}
1280*/
1281
1282
1283/**
1284 * Sets a function to execute when the audio file stops playing or removes it.
1285 * @function CB_AudioFile_API.SM2#onStop
1286 * @param {function|null} callbackFunction - The function (event listener) that we want to execute when the event is fired. No parameters will be received, being "this" the {@link CB_AudioFile_API.SM2} object. If a null value is used, the event will be removed.
1287 * @param {boolean} [keepOldFunction=true] - Defines whether we want to keep any possible previous event listener or not.
1288 * @returns {boolean} Returns whether the event has been set or not (removed).
1289 */
1290CB_AudioFile_API["SM2"].prototype.onStop = function(callbackFunction, keepOldFunction)
1291{
1292 //If not set, it keeps old function by default:
1293 if (typeof(keepOldFunction) === "undefined" || keepOldFunction === null) { keepOldFunction = true; }
1294
1295 if (typeof(callbackFunction) !== "function") { this.onStopFunction = null; return false; }
1296
1297 //If we don't want to keep the old function:
1298 if (!keepOldFunction)
1299 {
1300 this.onStopFunction = callbackFunction;
1301 }
1302 //...otherwise if we want to keep the old function, we keep it:
1303 else
1304 {
1305 var that = this;
1306 //Stores old function:
1307 var oldFunction = this.onStopFunction; //Stores old function of eventFunctionHolder.
1308 this.onStopFunction =
1309 function()
1310 {
1311 if (typeof(oldFunction) === "function") { oldFunction.call(that); }
1312 callbackFunction.call(that);
1313 };
1314 }
1315
1316 return true;
1317}
1318
1319
1320/**
1321 * Returns a number representing the percentage of the loading progress for the audio file (from 0 to 100, being 100 a complete loading progress). The way to calculate it internally may differ from one audio API to another and it is not totally reliable.
1322 * @function CB_AudioFile_API.SM2#getProgress
1323 * @returns {number} Returns a number representing the percentage of the loading progress for the audio file (from 0 to 100, being 100 a complete loading progress). The way to calculate it internally may differ from one audio API to another and it is not totally reliable.
1324 */
1325CB_AudioFile_API["SM2"].prototype.getProgress = function()
1326{
1327 if (this.status === CB_AudioFile.LOADED || this.status === CB_AudioFile.UNCHECKED || this.status === CB_AudioFile.CHECKING) { return 100; }
1328 else if (this.status === CB_AudioFile.UNLOADED) { return 0; }
1329
1330 var progress = 0;
1331
1332 //Calculates the progress (only if it is LOADING, FAILED or ABORTED):
1333 if (typeof(this.soundObject) !== "undefined" &amp;&amp; this.soundObject !== null &amp;&amp; typeof(this.soundObject.buffered) !== "undefined")
1334 {
1335 var duration = this.getDuration();
1336
1337 var bufferedLength = this.soundObject.buffered.length;
1338 var secondsBuffered = 0;
1339 for (var x = 0; x &lt; bufferedLength; x++)
1340 {
1341 secondsBuffered += this.soundObject.buffered[x].end - this.soundObject.buffered[x].start;
1342 }
1343
1344 if (duration === 0)
1345 {
1346 if (secondsBuffered > 0) { return 100; }
1347 else { return 0; }
1348 }
1349 else { progress = secondsBuffered / duration * 100; }
1350
1351 if (progress > 100) { progress = 100; }
1352 else if (progress &lt; 0) { progress = 0; }
1353 }
1354
1355 return progress;
1356}</pre>
1357 </article>
1358</section>
1359
1360
1361
1362
1363
1364 </div>
1365 </div>
1366
1367 <div class="clearfix"></div>
1368
1369
1370
1371</div>
1372</div>
1373
1374
1375 <div class="modal fade" id="searchResults">
1376 <div class="modal-dialog">
1377 <div class="modal-content">
1378 <div class="modal-header">
1379 <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
1380 <h4 class="modal-title">Search results</h4>
1381 </div>
1382 <div class="modal-body"></div>
1383 <div class="modal-footer">
1384 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1385 </div>
1386 </div><!-- /.modal-content -->
1387 </div><!-- /.modal-dialog -->
1388 </div>
1389
1390
1391<footer>
1392
1393
1394 <span class="copyright">
1395 <a href="printable/" target="_blank">See a more printer-friendly version</a><hr /><span style="color:#000000">© <address style="display:inline; font-style:normal;"><a href="https://crossbrowdy.com/" target="_blank">CrossBrowdy</a> API documentation</address> by <a href="https://joanalbamaldonado.com/" target="_blank">Joan Alba Maldonado</a> - <a href="https://creativecommons.org/licenses/by/4.0/" target="_blank">Creative Commons Attribution 4.0 International</a><br />DocStrap Copyright © 2012-2015 The contributors to the JSDoc3 and DocStrap projects.</span>
1396 </span>
1397
1398<span class="jsdoc-message">
1399 Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a>
1400
1401 on Mon Feb 3rd 2020
1402
1403 using the <a href="https://github.com/docstrap/docstrap">DocStrap template</a>.
1404</span>
1405</footer>
1406
1407<script src="scripts/docstrap.lib.js"></script>
1408<script src="scripts/toc.js"></script>
1409
1410 <script type="text/javascript" src="scripts/fulltext-search-ui.js"></script>
1411
1412
1413<script>
1414$( function () {
1415 $( "[id*='$']" ).each( function () {
1416 var $this = $( this );
1417
1418 $this.attr( "id", $this.attr( "id" ).replace( "$", "__" ) );
1419 } );
1420
1421 $( ".tutorial-section pre, .readme-section pre, pre.prettyprint.source" ).each( function () {
1422 var $this = $( this );
1423
1424 var example = $this.find( "code" );
1425 exampleText = example.html();
1426 var lang = /{@lang (.*?)}/.exec( exampleText );
1427 if ( lang && lang[1] ) {
1428 exampleText = exampleText.replace( lang[0], "" );
1429 example.html( exampleText );
1430 lang = lang[1];
1431 } else {
1432 var langClassMatch = example.parent()[0].className.match(/lang\-(\S+)/);
1433 lang = langClassMatch ? langClassMatch[1] : "javascript";
1434 }
1435
1436 if ( lang ) {
1437
1438 $this
1439 .addClass( "sunlight-highlight-" + lang )
1440 .addClass( "linenums" )
1441 .html( example.html() );
1442
1443 }
1444 } );
1445
1446 Sunlight.highlightAll( {
1447 lineNumbers : true,
1448 showMenu : true,
1449 enableDoclinks : true
1450 } );
1451
1452 $.catchAnchorLinks( {
1453 navbarOffset: 10
1454 } );
1455 $( "#toc" ).toc( {
1456 anchorName : function ( i, heading, prefix ) {
1457 return $( heading ).attr( "id" ) || ( prefix + i );
1458 },
1459 selectors : "#toc-content h1,#toc-content h2,#toc-content h3,#toc-content h4",
1460 showAndHide : false,
1461 smoothScrolling: true
1462 } );
1463
1464 $( "#main span[id^='toc']" ).addClass( "toc-shim" );
1465 $( '.dropdown-toggle' ).dropdown();
1466
1467 $( "table" ).each( function () {
1468 var $this = $( this );
1469 $this.addClass('table');
1470 } );
1471
1472} );
1473</script>
1474
1475
1476
1477<!--Navigation and Symbol Display-->
1478
1479<script>
1480 $( function () {
1481 $( '#main' ).localScroll( {
1482 offset : { top : 60 } //offset by the height of your header (give or take a few px, see what works for you)
1483 } );
1484 $( "dt.name" ).each( function () {
1485 var $this = $( this ).find("h4");
1486 var icon = $( "<i/>" ).addClass( "icon-plus-sign" ).addClass( "pull-right" ).addClass( "icon-white" );
1487 var dt = $(this);
1488 var children = dt.next( "dd" );
1489
1490 dt.prepend( icon ).css( {cursor : "pointer"} );
1491 dt.addClass( "member-collapsed" ).addClass( "member" );
1492
1493
1494 children.hide();
1495
1496 dt.children().on( "click", function () {
1497 children = dt.next( "dd" );
1498 children.slideToggle( "fast", function () {
1499
1500 if ( children.is( ":visible" ) ) {
1501 icon.addClass( "icon-minus-sign" ).removeClass( "icon-plus-sign" ).removeClass( "icon-white" );
1502 dt.addClass( "member-open" ).animate( "member-collapsed" );
1503 } else {
1504 icon.addClass( "icon-plus-sign" ).removeClass( "icon-minus-sign" ).addClass( "icon-white" );
1505 dt.addClass( "member-collapsed" ).removeClass( "member-open" );
1506 }
1507 } );
1508 } );
1509
1510 } );
1511 } );
1512</script>
1513
1514
1515<!--Google Analytics-->
1516
1517
1518
1519 <script type="text/javascript">
1520 $(document).ready(function() {
1521 SearcherDisplay.init();
1522 });
1523 </script>
1524
1525
1526</body>
1527</html>