1 /*
  2  * 	Copyright (C) 2012-2013 DFKI GmbH
  3  * 	Deutsches Forschungszentrum fuer Kuenstliche Intelligenz
  4  * 	German Research Center for Artificial Intelligence
  5  * 	http://www.dfki.de
  6  * 
  7  * 	Permission is hereby granted, free of charge, to any person obtaining a 
  8  * 	copy of this software and associated documentation files (the 
  9  * 	"Software"), to deal in the Software without restriction, including 
 10  * 	without limitation the rights to use, copy, modify, merge, publish, 
 11  * 	distribute, sublicense, and/or sell copies of the Software, and to 
 12  * 	permit persons to whom the Software is furnished to do so, subject to 
 13  * 	the following conditions:
 14  * 
 15  * 	The above copyright notice and this permission notice shall be included 
 16  * 	in all copies or substantial portions of the Software.
 17  * 
 18  * 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
 19  * 	OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
 20  * 	MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
 21  * 	IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 
 22  * 	CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
 23  * 	TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
 24  * 	SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 25  */
 26 
 27 newMediaPlugin = {
 28 		/**  @memberOf CordovaAudioOutput# */
 29 		initialize: function(callBack){//, mediaManager){//DISABLED this argument is currently un-used -> disabled
 30 			
 31 			/**  @memberOf CordovaAudioOutput# */
 32 			var _pluginName = 'codovaAudioOutput';
 33 			
 34 			//invoke the passed-in initializer-callback and export the public functions:
 35 			callBack({
 36 				/**
 37 				 * @public
 38 				 * @memberOf CordovaAudioOutput.prototype
 39 				 * @see mmir.MediaManager#playWAV
 40 				 */
 41 				playWAV: function(blob, successCallback, failureCallback){
 42 					try {
 43 						var blobURL = window.URL.createObjectURL(blob);
 44 						var my_media = new Media(
 45 								blobURL,
 46 								function(){ 
 47 //									console.log('WAV Audio created');
 48 
 49 									my_media.release();
 50 									if(successCallback){
 51 										successCallback();
 52 									}
 53 								},failureCallback
 54 						);
 55 
 56 						my_media.play();
 57 
 58 					} catch (e){
 59 						if(failureCallback){
 60 							failureCallback(e);
 61 						}
 62 					}
 63 				},
 64 				/**
 65 				 * @public
 66 				 * @memberOf CordovaAudioOutput.prototype
 67 				 * @see mmir.MediaManager#playURL
 68 				 */
 69 				playURL: function(url, successCallback, failureCallback){
 70 					try {
 71 //						console.log(url);
 72 						var my_media = new Media(
 73 								url, 
 74 								function(){ 
 75 //									console.log('Audio played');
 76 
 77 									my_media.release();
 78 									if(successCallback){
 79 										successCallback.apply(my_media,arguments);
 80 									}
 81 								} ,
 82 								failureCallback
 83 						);
 84 
 85 						my_media.play();
 86 					} catch (e){
 87 						if(failureCallback){
 88 							failureCallback.apply(my_media,arguments);
 89 						}
 90 					}
 91 				},
 92 				/**
 93 				 * @public
 94 				 * @memberOf CordovaAudioOutput.prototype
 95 				 * @see mmir.MediaManager#getURLAsAudio
 96 				 */
 97 				getURLAsAudio: function(url, onEnd, failureCallback, onCanPlay){
 98 					
 99 					try {
100 						
101 						/**
102 						 * @private
103 						 * @memberOf AudioCordovaImpl#
104 						 */
105 						var playStatus = 0;
106 						/**
107 						 * @private
108 						 * @memberOf AudioCordovaImpl#
109 						 */
110 						var my_media = new Media(
111 								url
112 								,null //DEBUG: function(){console.log('native onReady CB');}
113 								,failureCallback
114 								,function(status){
115 //									console.debug("media status change "+playStatus+" -> "+status+"  for: "+url);
116 
117 									playStatus = status;
118 
119 									if (status==1){
120 										if (onCanPlay){
121 											onCanPlay.apply(mediaImpl, arguments);
122 											onCanPlay = null;//remove onCanPlay callback after first invocation
123 										}
124 									} 
125 //									else if (status==2){
126 //									console.log("Audio started");
127 //									}
128 //									else if (status==3){
129 //									console.log("Audio paused");
130 //									}
131 									else if(status == 4){
132 										if (onEnd){
133 											onEnd.apply(mediaImpl, arguments);				    					 
134 										}
135 									}
136 								}
137 						);
138 						
139 						/**
140 						 * @private
141 						 * @memberOf AudioCordovaImpl#
142 						 */
143 						var enabled = true;
144 						
145 						
146 						/**
147 						 * The Audio abstraction that is returned by {@link mmir.MediaManager#getURLAsAudio}.
148 						 * 
149 						 * <p>
150 						 * NOTE: when an audio object is not used anymore, its {@link #release} method should
151 						 * 		 be called.
152 						 * 
153 						 * <p>
154 						 * This is the same interface as {@link mmir.env.media.AudioHtml5Impl}.
155 						 * 
156 						 * @class
157 						 * @name AudioCordovaImpl
158 						 * @memberOf mmir.env.media
159 						 * @implements mmir.env.media.IAudio
160 						 * @public
161 						 */
162 						var mediaImpl = {
163 								/**
164 								 * Play audio.
165 								 * 
166 								 * @inheritdoc
167 								 * @name play
168 								 * @memberOf mmir.env.media.AudioCordovaImpl.prototype
169 								 */
170 								play: function(){
171 									if (enabled){
172 										my_media.play();
173 									}
174 								},
175 								/**
176 								 * Stop playing audio.
177 								 * 
178 								 * @inheritdoc
179 								 * @name stop
180 								 * @memberOf mmir.env.media.AudioCordovaImpl.prototype
181 								 */
182 								stop: function(){
183 									//use "manual" stop instead of Cordova's stop
184 									//in order to allow "forgiving" behavior when audio is already stopped
185 									//	-> Cordova's stop() requires the audio to be playing, otherwise an error is thrown/triggered
186 
187 //									console.info('CordovaAudio.stop[state '+playStatus
188 //											+', duration '+my_media.duration
189 //											+', position '+my_media.position
190 ////											+', currentPosition '+my_media.getCurrentPosition()
191 //											+']: '+url);
192 									
193 									//only try to stop if playing and/or paused
194 									if(playStatus == 2 || playStatus == 3){
195 										my_media.stop();
196 									}
197 									
198 //									if(playStatus == 2){//playing
199 //										my_media.stop();
200 //									}
201 //									else if(playStatus == 3){//paused
202 //										my_media.seekTo(0);
203 //									}
204 ////									my_media.stop();
205 								},
206 								/**
207 								 * Enable audio (should only be used internally).
208 								 * 
209 								 * @inheritdoc
210 								 * @name enable
211 								 * @memberOf mmir.env.media.AudioCordovaImpl.prototype
212 								 */
213 								enable: function(){
214 									enabled = true;
215 								},
216 								/**
217 								 * Disable audio (should only be used internally).
218 								 * 
219 								 * @inheritdoc
220 								 * @name disable
221 								 * @memberOf mmir.env.media.AudioCordovaImpl.prototype
222 								 */
223 								disable: function(){
224 									if(enabled){
225 										this.stop();
226 										enabled = false;
227 									}
228 								},
229 								/**
230 								 * Release audio: should be called when the audio
231 								 * file is not used any more.
232 								 * 
233 								 * NOTE Android has limited resources available - not releasing resources
234 								 *      may result in not being able to instantiate new (audio) resources.
235 								 * 
236 								 * @inheritdoc
237 								 * @name release
238 								 * @memberOf mmir.env.media.AudioCordovaImpl.prototype
239 								 */
240 								release: function(){
241 									if(enabled && ! this.isPaused()){
242 										this.stop();
243 									}
244 									enabled= false;
245 									if(my_media){
246 										my_media.release();
247 									}
248 
249 								},
250 								/**
251 								 * Set the volume of this audio file
252 								 * 
253 								 * @param {Number} value
254 								 * 			the new value for the volume:
255 								 * 			a number between [0.0, 1.0]
256 								 * 
257 								 * @inheritdoc
258 								 * @name setVolume
259 								 * @memberOf mmir.env.media.AudioCordovaImpl.prototype
260 								 */
261 								setVolume: function(value){
262 									if(my_media){
263 										my_media.setVolume(value);
264 									}
265 								},
266 								/**
267 								 * Get the duration of the audio file
268 								 * 
269 								 * @returns {Number} the duration in MS (or -1 if unknown)
270 								 * 
271 								 * @inheritdoc
272 								 * @name getDuration
273 								 * @memberOf mmir.env.media.AudioCordovaImpl.prototype
274 								 */
275 								getDuration: function(){
276 									if(my_media){
277 										return my_media.duration;
278 									}
279 									return -1;
280 								},
281 								/**
282 								 * Check if audio is currently paused.
283 								 * 
284 								 * NOTE: "paused" is a different status than "stopped".
285 								 * 
286 								 * @returns {Boolean} TRUE if paused, FALSE otherwise
287 								 * 
288 								 * @inheritdoc
289 								 * @name isPaused
290 								 * @memberOf mmir.env.media.AudioCordovaImpl.prototype
291 								 */
292 								isPaused: function(){
293 									if(my_media){
294 										return playStatus == 3;
295 									}
296 									return false;
297 								},
298 								/**
299 								 * Check if audio is currently enabled
300 								 * 
301 								 * @returns {Boolean} TRUE if enabled
302 								 * 
303 								 * @inheritdoc
304 								 * @name isEnabled
305 								 * @memberOf mmir.env.media.AudioCordovaImpl.prototype
306 								 */
307 								isEnabled: function(){
308 									return enabled;
309 								}
310 						};
311 
312 						//WORK-AROUND for Android: need to invoke a method on the Media object in
313 						//							order to trigger the on-init callback.
314 						my_media.seekTo(0);
315 
316 
317 						return mediaImpl;
318 
319 					} catch (e){
320 						console.error(e);
321 						if(failureCallback){
322 							failureCallback(e);
323 						}
324 					}
325 				}//END: getURLAsAudio
326 				
327 			});//END: callBack({...
328 		}
329 };