1 /** 2 * The Render Engine 3 * HTML5 4 * 5 * @fileoverview The HTML5 sound system. 6 * 7 * @author: Brett Fattori (brettf@renderengine.com) 8 * @author: $Author: bfattori $ 9 * @version: $Revision: 1555 $ 10 * 11 * Copyright (c) 2011 Brett Fattori (brettf@renderengine.com) 12 * 13 * Permission is hereby granted, free of charge, to any person obtaining a copy 14 * of this software and associated documentation files (the "Software"), to deal 15 * in the Software without restriction, including without limitation the rights 16 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 * copies of the Software, and to permit persons to whom the Software is 18 * furnished to do so, subject to the following conditions: 19 * 20 * The above copyright notice and this permission notice shall be included in 21 * all copies or substantial portions of the Software. 22 * 23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 * THE SOFTWARE. 30 * 31 */ 32 33 // The class this file defines and its required classes 34 R.Engine.define({ 35 "class":"R.sound.HTML5", 36 "requires":[ 37 "R.sound.AbstractSoundSystem" 38 ] 39 }); 40 41 /** 42 * @class Initializes the HTML5 sound system. The sound types supported by the HTML5 sound system 43 * are determined by file extension on the URL: 44 * <ul> 45 * <li>mp3 - Audio MP3 format (Safari, Chrome, IE9)</li> 46 * <li>aac - Apple's AAC format (Safari)</li> 47 * <li>ogg - Ogg Vorbis format (Firefox, Chrome, Opera)</li> 48 * <li>pcm - WebM audio-only format (Firefox)</li> 49 * <li>wav - Wave audio format (Firefox, Safari, Opera, IE9)</li> 50 * </ul> 51 * 52 * @constructor 53 * @extends R.sound.AbstractSoundSystem 54 */ 55 R.sound.HTML5 = function () { 56 return R.sound.AbstractSoundSystem.extend(/** @scope R.sound.HTML5.prototype */{ 57 58 supported:false, 59 types:null, 60 audioRoot:null, 61 62 /** @private */ 63 constructor:function () { 64 this.base(); 65 66 // Check for audio support on this browser before doing anything more 67 if (!R.engine.Support.sysInfo().support.audio) { 68 this.supported = false; 69 return; 70 } 71 72 // Get an audio element we can use to determine if the type is supported 73 this.audioRoot = new Audio(""); 74 this.supported = true; 75 76 // Interrogate the supported audio types 77 this.types = { 78 'mp3':this.verifyType("audio/mpeg"), 79 'aac':this.verifyType("audio/mp4; codecs='mp4a.40.2'"), 80 'ogg':this.verifyType("audio/ogg; codecs='vorbis'"), 81 'pcm':this.verifyType("audio/webm; codecs='vorbis'"), 82 'wav':this.verifyType("audio/x-wav; codecs='1'") || this.verifyType("audio/wave; codecs='1'") || 83 this.verifyType("audio/wav; codecs='1'") || this.verifyType("audio/x-pn-wav; codecs='1'") 84 }; 85 }, 86 87 /** 88 * Verify if the given type can be played 89 * @param mime 90 */ 91 verifyType:function (mime) { 92 return (this.audioRoot.canPlayType(mime) != ""); 93 }, 94 95 /** 96 * Retrieve the sound from the network, when the sound system is ready, and create the sound object. 97 * @param resourceLoader {R.resources.loades.SoundLoader} The sound resource loader 98 * @param name {String} The name of the sound object 99 * @param url {String} The URL of the sound to load 100 * @return {R.resources.types.Sound} The sound object 101 * @private 102 */ 103 retrieveSound:function (resourceLoader, name, url) { 104 // First check to see if we've already got an audio element cached 105 var sound = this.base(resourceLoader, name, url); 106 107 if (sound.getSoundObject() == null) { 108 // Nope, this is a new sound object 109 110 // Check if the browser supports this sound format 111 var ext = url.split("/"); 112 ext = ext[ext.length - 1].split(".")[1]; 113 if (!this.types[ext]) { 114 // The sound type is not supported 115 sound.setSupportedTypeFlag(false); 116 return sound; 117 } 118 119 // The type is supported, load the audio element and make sure it's not set to autoplay 120 var audio = new Audio(url); 121 audio.autoplay = false; 122 audio.volume = 0.5; 123 124 // Store the sound object 125 sound.setSoundObject(audio); 126 } 127 128 return sound; 129 }, 130 131 /** 132 * Destroy the given sound object 133 * @param sound {R.resources.types.Sound} The sound object 134 */ 135 destroySound:function (sound) { 136 if (!(this.supported || this.getSoundReadyState(sound))) { 137 return; 138 } 139 sound.pause(); 140 sound.src = ""; 141 }, 142 143 /** 144 * Play the given sound object 145 * @param sound {R.resources.types.Sound} The sound object 146 */ 147 playSound:function (sound) { 148 if (!(this.supported || this.getSoundReadyState(sound))) { 149 return; 150 } 151 sound.play(); 152 }, 153 154 /** 155 * Stop the given sound object 156 * @param sound {R.resources.types.Sound} The sound object 157 */ 158 stopSound:function (sound) { 159 if (!(this.supported || this.getSoundReadyState(sound))) { 160 return; 161 } 162 sound.pause(); 163 sound.currentTime = 0; 164 }, 165 166 /** 167 * Pause the given sound object 168 * @param sound {R.resources.types.Sound} The sound object 169 */ 170 pauseSound:function (sound) { 171 if (!(this.supported || this.getSoundReadyState(sound))) { 172 return; 173 } 174 sound.pause(); 175 }, 176 177 /** 178 * Resume the given sound object 179 * @param sound {R.resources.types.Sound} The sound object 180 */ 181 resumeSound:function (sound) { 182 if (!(this.supported || this.getSoundReadyState(sound))) { 183 return; 184 } 185 if (sound.paused) { 186 sound.play(); 187 } 188 }, 189 190 /** 191 * Mute the given sound object 192 * @param sound {R.resources.types.Sound} The sound object 193 */ 194 muteSound:function (sound) { 195 if (!(this.supported || this.getSoundReadyState(sound))) { 196 return; 197 } 198 sound.muted = true; 199 }, 200 201 /** 202 * Unmute the given sound object 203 * @param sound {R.resources.types.Sound} The sound object 204 */ 205 unmuteSound:function (sound) { 206 if (!(this.supported || this.getSoundReadyState(sound))) { 207 return; 208 } 209 sound.muted = false; 210 }, 211 212 /** 213 * Set the volume of the given sound object 214 * @param sound {R.resources.types.Sound} The sound object 215 * @param volume {Number} A value between 0 and 100, with 0 being muted 216 */ 217 setSoundVolume:function (sound, volume) { 218 if (!(this.supported || this.getSoundReadyState(sound))) { 219 return; 220 } 221 sound.volume = volume / 100; 222 }, 223 224 /** 225 * Pan the given sound object from left to right (unsupported in HTML5) 226 * @param sound {R.resources.types.Sound} The sound object 227 * @param pan {Number} A value between -100 and 100, with -100 being full left 228 * and zero being center 229 */ 230 setSoundPan:function (sound, pan) { 231 return; 232 }, 233 234 /** 235 * Set the position, within the sound's length, to play at 236 * @param sound {R.resources.types.Sound} The sound object 237 * @param millisecondOffset {Number} The millisecond offset from the start of 238 * the sounds duration 239 */ 240 setSoundPosition:function (sound, millisecondOffset) { 241 if (!(this.supported || this.getSoundReadyState(sound))) { 242 return; 243 } 244 sound.currentTime = Math.floor(millisecondOffset / 1000); 245 }, 246 247 /** 248 * Get the position, in milliseconds, within a playing or paused sound 249 * @param sound {R.resources.types.Sound} The sound object 250 * @return {Number} 251 */ 252 getSoundPosition:function (sound) { 253 if (!(this.supported || this.getSoundReadyState(sound))) { 254 return 0; 255 } 256 return sound.currentTime * 1000; 257 }, 258 259 /** 260 * Get the size of the sound object, in bytes (unsupported in HTML5) 261 * @param sound {R.resources.types.Sound} The sound object 262 * @return {Number} 263 */ 264 getSoundSize:function (sound) { 265 return 0; 266 }, 267 268 /** 269 * Get the length (duration) of the sound object, in milliseconds 270 * @param sound {R.resources.types.Sound} The sound object 271 * @return {Number} 272 */ 273 getSoundDuration:function (sound) { 274 if (!(this.supported || this.getSoundReadyState(sound))) { 275 return 0; 276 } 277 var d = sound.duration; 278 return (isNaN(d) ? 0 : d); 279 }, 280 281 /** 282 * Determine if the sound object is ready to be used 283 * @param sound {R.resources.types.Sound} The sound object 284 * @return {Boolean} <code>true</code> if the sound is ready 285 */ 286 getSoundReadyState:function (sound) { 287 if (!this.supported) { 288 return true; 289 } 290 if (!sound) { 291 return false; 292 } 293 return (sound.readyState >= R.sound.HTML5.HAVE_ENOUGH_DATA); 294 } 295 296 }, { 297 HAVE_NOTHING:0, 298 HAVE_METADATA:1, 299 HAVE_CURRENT_DATA:2, 300 HAVE_FUTURE_DATA:3, 301 HAVE_ENOUGH_DATA:4 302 }); 303 304 }; 305