// ---------------------------------------------------------------------------- // Buzz, a Javascript HTML5 Audio library // v 1.0.4 beta // Licensed under the MIT license. // http://buzz.jaysalvat.com/ // ---------------------------------------------------------------------------- // Copyright (C) 2011 Jay Salvat // http://jaysalvat.com/ // ---------------------------------------------------------------------------- // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files ( the "Software" ), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // ---------------------------------------------------------------------------- var buzz = { defaults: { autoplay: false, duration: 5000, formats: [], loop: false, placeholder: '--', preload: 'metadata', volume: 80 }, types: { 'mp3': 'audio/mpeg', 'ogg': 'audio/ogg', 'wav': 'audio/wav', 'aac': 'audio/aac', 'm4a': 'audio/x-m4a' }, sounds: [], el: document.createElement( 'audio' ), sound: function( src, options ) { options = options || {}; var pid = 0, events = [], eventsOnce = {}, supported = buzz.isSupported(); // publics this.load = function() { if ( !supported ) { return this; } this.sound.load(); return this; }; this.play = function() { if ( !supported ) { return this; } this.sound.play(); return this; }; this.togglePlay = function() { if ( !supported ) { return this; } if ( this.sound.paused ) { this.sound.play(); } else { this.sound.pause(); } return this; }; this.pause = function() { if ( !supported ) { return this; } this.sound.pause(); return this; }; this.isPaused = function() { if ( !supported ) { return null; } return this.sound.paused; }; this.stop = function() { if ( !supported ) { return this; } this.setTime( this.getDuration() ); this.sound.pause(); return this; }; this.isEnded = function() { if ( !supported ) { return null; } return this.sound.ended; }; this.loop = function() { if ( !supported ) { return this; } this.sound.loop = 'loop'; this.bind( 'ended.buzzloop', function() { this.currentTime = 0; this.play(); }); return this; }; this.unloop = function() { if ( !supported ) { return this; } this.sound.removeAttribute( 'loop' ); this.unbind( 'ended.buzzloop' ); return this; }; this.mute = function() { if ( !supported ) { return this; } this.sound.muted = true; return this; }; this.unmute = function() { if ( !supported ) { return this; } this.sound.muted = false; return this; }; this.toggleMute = function() { if ( !supported ) { return this; } this.sound.muted = !this.sound.muted; return this; }; this.isMuted = function() { if ( !supported ) { return null; } return this.sound.muted; }; this.setVolume = function( volume ) { if ( !supported ) { return this; } if ( volume < 0 ) { volume = 0; } if ( volume > 100 ) { volume = 100; } this.volume = volume; this.sound.volume = volume / 100; return this; }; this.getVolume = function() { if ( !supported ) { return this; } return this.volume; }; this.increaseVolume = function( value ) { return this.setVolume( this.volume + ( value || 1 ) ); }; this.decreaseVolume = function( value ) { return this.setVolume( this.volume - ( value || 1 ) ); }; this.setTime = function( time ) { if ( !supported ) { return this; } this.whenReady( function() { this.sound.currentTime = time; }); return this; }; this.getTime = function() { if ( !supported ) { return null; } var time = Math.round( this.sound.currentTime * 100 ) / 100; return isNaN( time ) ? buzz.defaults.placeholder : time; }; this.setPercent = function( percent ) { if ( !supported ) { return this; } return this.setTime( buzz.fromPercent( percent, this.sound.duration ) ); }; this.getPercent = function() { if ( !supported ) { return null; } var percent = Math.round( buzz.toPercent( this.sound.currentTime, this.sound.duration ) ); return isNaN( percent ) ? buzz.defaults.placeholder : percent; }; this.setSpeed = function( duration ) { if ( !supported ) { return this; } this.sound.playbackRate = duration; }; this.getSpeed = function() { if ( !supported ) { return null; } return this.sound.playbackRate; }; this.getDuration = function() { if ( !supported ) { return null; } var duration = Math.round( this.sound.duration * 100 ) / 100; return isNaN( duration ) ? buzz.defaults.placeholder : duration; }; this.getPlayed = function() { if ( !supported ) { return null; } return timerangeToArray( this.sound.played ); }; this.getBuffered = function() { if ( !supported ) { return null; } return timerangeToArray( this.sound.buffered ); }; this.getSeekable = function() { if ( !supported ) { return null; } return timerangeToArray( this.sound.seekable ); }; this.getErrorCode = function() { if ( supported && this.sound.error ) { return this.sound.error.code; } return 0; }; this.getErrorMessage = function() { if ( !supported ) { return null; } switch( this.getErrorCode() ) { case 1: return 'MEDIA_ERR_ABORTED'; case 2: return 'MEDIA_ERR_NETWORK'; case 3: return 'MEDIA_ERR_DECODE'; case 4: return 'MEDIA_ERR_SRC_NOT_SUPPORTED'; default: return null; } }; this.getStateCode = function() { if ( !supported ) { return null; } return this.sound.readyState; }; this.getStateMessage = function() { if ( !supported ) { return null; } switch( this.getStateCode() ) { case 0: return 'HAVE_NOTHING'; case 1: return 'HAVE_METADATA'; case 2: return 'HAVE_CURRENT_DATA'; case 3: return 'HAVE_FUTURE_DATA'; case 4: return 'HAVE_ENOUGH_DATA'; default: return null; } }; this.getNetworkStateCode = function() { if ( !supported ) { return null; } return this.sound.networkState; }; this.getNetworkStateMessage = function() { if ( !supported ) { return null; } switch( this.getNetworkStateCode() ) { case 0: return 'NETWORK_EMPTY'; case 1: return 'NETWORK_IDLE'; case 2: return 'NETWORK_LOADING'; case 3: return 'NETWORK_NO_SOURCE'; default: return null; } }; this.set = function( key, value ) { if ( !supported ) { return this; } this.sound[ key ] = value; return this; }; this.get = function( key ) { if ( !supported ) { return null; } return key ? this.sound[ key ] : this.sound; }; this.bind = function( types, func ) { if ( !supported ) { return this; } types = types.split( ' ' ); var that = this, efunc = function( e ) { func.call( that, e ); }; for( var t = 0; t < types.length; t++ ) { var type = types[ t ], idx = type; type = idx.split( '.' )[ 0 ]; events.push( { idx: idx, func: efunc } ); this.sound.addEventListener( type, efunc, true ); } return this; }; this.unbind = function( types ) { if ( !supported ) { return this; } types = types.split( ' ' ); for( var t = 0; t < types.length; t++ ) { var idx = types[ t ], type = idx.split( '.' )[ 0 ]; for( var i = 0; i < events.length; i++ ) { var namespace = events[ i ].idx.split( '.' ); if ( events[ i ].idx == idx || ( namespace[ 1 ] && namespace[ 1 ] == idx.replace( '.', '' ) ) ) { this.sound.removeEventListener( type, events[ i ].func, true ); delete events[ i ]; } } } return this; }; this.bindOnce = function( type, func ) { if ( !supported ) { return this; } var that = this; eventsOnce[ pid++ ] = false; this.bind( pid + type, function() { if ( !eventsOnce[ pid ] ) { eventsOnce[ pid ] = true; func.call( that ); } that.unbind( pid + type ); }); }; this.trigger = function( types ) { if ( !supported ) { return this; } types = types.split( ' ' ); for( var t = 0; t < types.length; t++ ) { var idx = types[ t ]; for( var i = 0; i < events.length; i++ ) { var eventType = events[ i ].idx.split( '.' ); if ( events[ i ].idx == idx || ( eventType[ 0 ] && eventType[ 0 ] == idx.replace( '.', '' ) ) ) { var evt = document.createEvent('HTMLEvents'); evt.initEvent( eventType[ 0 ], false, true ); this.sound.dispatchEvent( evt ); } } } return this; }; this.fadeTo = function( to, duration, callback ) { if ( !supported ) { return this; } if ( duration instanceof Function ) { callback = duration; duration = buzz.defaults.duration; } else { duration = duration || buzz.defaults.duration; } var from = this.volume, delay = duration / Math.abs( from - to ), that = this; this.play(); function doFade() { setTimeout( function() { if ( from < to && that.volume < to ) { that.setVolume( that.volume += 1 ); doFade(); } else if ( from > to && that.volume > to ) { that.setVolume( that.volume -= 1 ); doFade(); } else if ( callback instanceof Function ) { callback.apply( that ); } }, delay ); } this.whenReady( function() { doFade(); }); return this; }; this.fadeIn = function( duration, callback ) { if ( !supported ) { return this; } return this.setVolume(0).fadeTo( 100, duration, callback ); }; this.fadeOut = function( duration, callback ) { if ( !supported ) { return this; } return this.fadeTo( 0, duration, callback ); }; this.fadeWith = function( sound, duration ) { if ( !supported ) { return this; } this.fadeOut( duration, function() { this.stop(); }); sound.play().fadeIn( duration ); return this; }; this.whenReady = function( func ) { if ( !supported ) { return null; } var that = this; if ( this.sound.readyState === 0 ) { this.bind( 'canplay.buzzwhenready', function() { func.call( that ); }); } else { func.call( that ); } }; // privates function timerangeToArray( timeRange ) { var array = [], length = timeRange.length - 1; for( var i = 0; i <= length; i++ ) { array.push({ start: timeRange.start( length ), end: timeRange.end( length ) }); } return array; } function getExt( filename ) { return filename.split('.').pop(); } function addSource( sound, src ) { var source = document.createElement( 'source' ); source.src = src; if ( buzz.types[ getExt( src ) ] ) { source.type = buzz.types[ getExt( src ) ]; } sound.appendChild( source ); } // init if ( supported ) { for(var i in buzz.defaults ) { if(buzz.defaults.hasOwnProperty(i)) { options[ i ] = options[ i ] || buzz.defaults[ i ]; } } this.sound = document.createElement( 'audio' ); if ( src instanceof Array ) { for( var j in src ) { if(src.hasOwnProperty(j)) { addSource( this.sound, src[ j ] ); } } } else if ( options.formats.length ) { for( var k in options.formats ) { if(options.formats.hasOwnProperty(k)) { addSource( this.sound, src + '.' + options.formats[ k ] ); } } } else { addSource( this.sound, src ); } if ( options.loop ) { this.loop(); } if ( options.autoplay ) { this.sound.autoplay = 'autoplay'; } if ( options.preload === true ) { this.sound.preload = 'auto'; } else if ( options.preload === false ) { this.sound.preload = 'none'; } else { this.sound.preload = options.preload; } this.setVolume( options.volume ); buzz.sounds.push( this ); } }, group: function( sounds ) { sounds = argsToArray( sounds, arguments ); // publics this.getSounds = function() { return sounds; }; this.add = function( soundArray ) { soundArray = argsToArray( soundArray, arguments ); for( var a = 0; a < soundArray.length; a++ ) { sounds.push( soundArray[ a ] ); } }; this.remove = function( soundArray ) { soundArray = argsToArray( soundArray, arguments ); for( var a = 0; a < soundArray.length; a++ ) { for( var i = 0; i < sounds.length; i++ ) { if ( sounds[ i ] == soundArray[ a ] ) { delete sounds[ i ]; break; } } } }; this.load = function() { fn( 'load' ); return this; }; this.play = function() { fn( 'play' ); return this; }; this.togglePlay = function( ) { fn( 'togglePlay' ); return this; }; this.pause = function( time ) { fn( 'pause', time ); return this; }; this.stop = function() { fn( 'stop' ); return this; }; this.mute = function() { fn( 'mute' ); return this; }; this.unmute = function() { fn( 'unmute' ); return this; }; this.toggleMute = function() { fn( 'toggleMute' ); return this; }; this.setVolume = function( volume ) { fn( 'setVolume', volume ); return this; }; this.increaseVolume = function( value ) { fn( 'increaseVolume', value ); return this; }; this.decreaseVolume = function( value ) { fn( 'decreaseVolume', value ); return this; }; this.loop = function() { fn( 'loop' ); return this; }; this.unloop = function() { fn( 'unloop' ); return this; }; this.setTime = function( time ) { fn( 'setTime', time ); return this; }; this.setduration = function( duration ) { fn( 'setduration', duration ); return this; }; this.set = function( key, value ) { fn( 'set', key, value ); return this; }; this.bind = function( type, func ) { fn( 'bind', type, func ); return this; }; this.unbind = function( type ) { fn( 'unbind', type ); return this; }; this.bindOnce = function( type, func ) { fn( 'bindOnce', type, func ); return this; }; this.trigger = function( type ) { fn( 'trigger', type ); return this; }; this.fade = function( from, to, duration, callback ) { fn( 'fade', from, to, duration, callback ); return this; }; this.fadeIn = function( duration, callback ) { fn( 'fadeIn', duration, callback ); return this; }; this.fadeOut = function( duration, callback ) { fn( 'fadeOut', duration, callback ); return this; }; // privates function fn() { var args = argsToArray( null, arguments ), func = args.shift(); for( var i = 0; i < sounds.length; i++ ) { sounds[ i ][ func ].apply( sounds[ i ], args ); } } function argsToArray( array, args ) { return ( array instanceof Array ) ? array : Array.prototype.slice.call( args ); } }, all: function() { return new buzz.group( buzz.sounds ); }, isSupported: function() { return !!buzz.el.canPlayType; }, isOGGSupported: function() { return !!buzz.el.canPlayType && buzz.el.canPlayType( 'audio/ogg; codecs="vorbis"' ); }, isWAVSupported: function() { return !!buzz.el.canPlayType && buzz.el.canPlayType( 'audio/wav; codecs="1"' ); }, isMP3Supported: function() { return !!buzz.el.canPlayType && buzz.el.canPlayType( 'audio/mpeg;' ); }, isAACSupported: function() { return !!buzz.el.canPlayType && ( buzz.el.canPlayType( 'audio/x-m4a;' ) || buzz.el.canPlayType( 'audio/aac;' ) ); }, toTimer: function( time, withHours ) { var h, m, s; h = Math.floor( time / 3600 ); h = isNaN( h ) ? '--' : ( h >= 10 ) ? h : '0' + h; m = withHours ? Math.floor( time / 60 % 60 ) : Math.floor( time / 60 ); m = isNaN( m ) ? '--' : ( m >= 10 ) ? m : '0' + m; s = Math.floor( time % 60 ); s = isNaN( s ) ? '--' : ( s >= 10 ) ? s : '0' + s; return withHours ? h + ':' + m + ':' + s : m + ':' + s; }, fromTimer: function( time ) { var splits = time.toString().split( ':' ); if ( splits && splits.length == 3 ) { time = ( parseInt( splits[ 0 ], 10 ) * 3600 ) + ( parseInt(splits[ 1 ], 10 ) * 60 ) + parseInt( splits[ 2 ], 10 ); } if ( splits && splits.length == 2 ) { time = ( parseInt( splits[ 0 ], 10 ) * 60 ) + parseInt( splits[ 1 ], 10 ); } return time; }, toPercent: function( value, total, decimal ) { var r = Math.pow( 10, decimal || 0 ); return Math.round( ( ( value * 100 ) / total ) * r ) / r; }, fromPercent: function( percent, total, decimal ) { var r = Math.pow( 10, decimal || 0 ); return Math.round( ( ( total / 100 ) * percent ) * r ) / r; } };