diff --git a/dependencies/howler.min.js b/dependencies/howler.min.js new file mode 100644 index 000000000..353e07726 --- /dev/null +++ b/dependencies/howler.min.js @@ -0,0 +1,4 @@ +/*! howler.js v2.0.0 | (c) 2013-2016, James Simpson of GoldFire Studios | MIT License | howlerjs.com */ +!function(){"use strict";var e=function(){this.init()};e.prototype={init:function(){var e=this||n;return e._codecs={},e._howls=[],e._muted=!1,e._volume=1,e._canPlayEvent="canplaythrough",e._navigator="undefined"!=typeof window&&window.navigator?window.navigator:null,e.masterGain=null,e.noAudio=!1,e.usingWebAudio=!0,e.autoSuspend=!0,e.ctx=null,e.mobileAutoEnable=!0,e._setup(),e},volume:function(e){var o=this||n;if(e=parseFloat(e),o.ctx||_(),"undefined"!=typeof e&&e>=0&&e<=1){if(o._volume=e,o._muted)return o;o.usingWebAudio&&(o.masterGain.gain.value=e);for(var t=0;t=0;o--)e._howls[o].unload();return e.usingWebAudio&&"undefined"!=typeof e.ctx.close&&(e.ctx.close(),e.ctx=null,_()),e},codecs:function(e){return(this||n)._codecs[e]},_setup:function(){var e=this||n;return e.state=e.ctx?e.ctx.state||"running":"running",e._autoSuspend(),e.noAudio||e._setupCodecs(),e},_setupCodecs:function(){var e=this||n,o="undefined"!=typeof Audio?new Audio:null;if(!o||"function"!=typeof o.canPlayType)return e;var t=o.canPlayType("audio/mpeg;").replace(/^no$/,""),r=e._navigator&&e._navigator.userAgent.match(/OPR\/([0-6].)/g),u=r&&parseInt(r[0].split("/")[1],10)<33;return e._codecs={mp3:!(u||!t&&!o.canPlayType("audio/mp3;").replace(/^no$/,"")),mpeg:!!t,opus:!!o.canPlayType('audio/ogg; codecs="opus"').replace(/^no$/,""),ogg:!!o.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),oga:!!o.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),wav:!!o.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),aac:!!o.canPlayType("audio/aac;").replace(/^no$/,""),caf:!!o.canPlayType("audio/x-caf;").replace(/^no$/,""),m4a:!!(o.canPlayType("audio/x-m4a;")||o.canPlayType("audio/m4a;")||o.canPlayType("audio/aac;")).replace(/^no$/,""),mp4:!!(o.canPlayType("audio/x-mp4;")||o.canPlayType("audio/mp4;")||o.canPlayType("audio/aac;")).replace(/^no$/,""),weba:!!o.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/,""),webm:!!o.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/,""),dolby:!!o.canPlayType('audio/mp4; codecs="ec-3"').replace(/^no$/,"")},e},_enableMobileAudio:function(){var e=this||n,o=/iPhone|iPad|iPod|Android|BlackBerry|BB10|Silk|Mobi/i.test(e._navigator&&e._navigator.userAgent),t=!!("ontouchend"in window||e._navigator&&e._navigator.maxTouchPoints>0||e._navigator&&e._navigator.msMaxTouchPoints>0);if(!e._mobileEnabled&&e.ctx&&(o||t)){e._mobileEnabled=!1,e._mobileUnloaded||44100===e.ctx.sampleRate||(e._mobileUnloaded=!0,e.unload()),e._scratchBuffer=e.ctx.createBuffer(1,1,22050);var r=function(){var n=e.ctx.createBufferSource();n.buffer=e._scratchBuffer,n.connect(e.ctx.destination),"undefined"==typeof n.start?n.noteOn(0):n.start(0),n.onended=function(){n.disconnect(0),e._mobileEnabled=!0,e.mobileAutoEnable=!1,document.removeEventListener("touchend",r,!0)}};return document.addEventListener("touchend",r,!0),e}},_autoSuspend:function(){var e=this;if(e.autoSuspend&&e.ctx&&"undefined"!=typeof e.ctx.suspend&&n.usingWebAudio){for(var o=0;o0?d._seek:t._sprite[e][0]/1e3,_=(t._sprite[e][0]+t._sprite[e][1])/1e3-i,s=1e3*_/Math.abs(d._rate);d._paused=!1,d._ended=!1,d._sprite=e,d._seek=i,d._start=t._sprite[e][0]/1e3,d._stop=(t._sprite[e][0]+t._sprite[e][1])/1e3,d._loop=!(!d._loop&&!t._sprite[e][2]);var l=d._node;if(t._webAudio){var f=function(){t._refreshBuffer(d);var e=d._muted||t._muted?0:d._volume;l.gain.setValueAtTime(e,n.ctx.currentTime),d._playStart=n.ctx.currentTime,"undefined"==typeof l.bufferSource.start?d._loop?l.bufferSource.noteGrainOn(0,i,86400):l.bufferSource.noteGrainOn(0,i,_):d._loop?l.bufferSource.start(0,i,86400):l.bufferSource.start(0,i,_),s!==1/0&&(t._endTimers[d._id]=setTimeout(t._ended.bind(t,d),s)),o||setTimeout(function(){t._emit("play",d._id)},0)};"loaded"===t._state?f():(t.once("load",f,d._id),t._clearTimer(d._id))}else{var c=function(){l.currentTime=i,l.muted=d._muted||t._muted||n._muted||l.muted,l.volume=d._volume*n.volume(),l.playbackRate=d._rate,setTimeout(function(){l.play(),s!==1/0&&(t._endTimers[d._id]=setTimeout(t._ended.bind(t,d),s)),o||t._emit("play",d._id)},0)},p="loaded"===t._state&&(window&&window.ejecta||!l.readyState&&n._navigator.isCocoonJS);if(4===l.readyState||p)c();else{var m=function(){c(),l.removeEventListener(n._canPlayEvent,m,!1)};l.addEventListener(n._canPlayEvent,m,!1),t._clearTimer(d._id)}}return d._id},pause:function(e){var n=this;if("loaded"!==n._state)return n._queue.push({event:"pause",action:function(){n.pause(e)}}),n;for(var o=n._getSoundIds(e),t=0;t=0?o=parseInt(r[0],10):e=parseFloat(r[0])}else r.length>=2&&(e=parseFloat(r[0]),o=parseInt(r[1],10));var d;if(!("undefined"!=typeof e&&e>=0&&e<=1))return d=o?t._soundById(o):t._sounds[0],d?d._volume:0;if("loaded"!==t._state)return t._queue.push({event:"volume",action:function(){t.volume.apply(t,r)}}),t;"undefined"==typeof o&&(t._volume=e),o=t._getSoundIds(o);for(var i=0;io?"out":"in",i=a/.01,_=t/i;if("loaded"!==u._state)return u._queue.push({event:"fade",action:function(){u.fade(e,o,t,r)}}),u;u.volume(e,r);for(var s=u._getSoundIds(r),l=0;l=0?o=parseInt(r[0],10):e=parseFloat(r[0])}else 2===r.length&&(e=parseFloat(r[0]),o=parseInt(r[1],10));var d;if("number"!=typeof e)return d=t._soundById(o),d?d._rate:t._rate;if("loaded"!==t._state)return t._queue.push({event:"rate",action:function(){t.rate.apply(t,r)}}),t;"undefined"==typeof o&&(t._rate=e),o=t._getSoundIds(o);for(var i=0;i=0?o=parseInt(r[0],10):(o=t._sounds[0]._id,e=parseFloat(r[0]))}else 2===r.length&&(e=parseFloat(r[0]),o=parseInt(r[1],10));if("undefined"==typeof o)return t;if("loaded"!==t._state)return t._queue.push({event:"seek",action:function(){t.seek.apply(t,r)}}),t;var d=t._soundById(o);if(d){if(!("number"==typeof e&&e>=0)){if(t._webAudio){var i=t.playing(o)?n.ctx.currentTime-d._playStart:0,_=d._rateSeek?d._rateSeek-d._seek:0;return d._seek+(_+i*Math.abs(d._rate))}return d._node.currentTime}var s=t.playing(o);s&&t.pause(o,!0),d._seek=e,d._ended=!1,t._clearTimer(o),s&&t.play(o,!0),!t._webAudio&&d._node&&(d._node.currentTime=e),t._emit("seek",o)}return t},playing:function(e){var n=this;if("number"==typeof e){var o=n._soundById(e);return!!o&&!o._paused}for(var t=0;t=0&&n._howls.splice(u,1)}var a=!0;for(t=0;t=0;u--)r[u].id&&r[u].id!==n&&"load"!==e||(setTimeout(function(e){e.call(this,n,o)}.bind(t,r[u].fn),0),r[u].once&&t.off(e,r[u].fn,r[u].id));return t},_loadQueue:function(){var e=this;if(e._queue.length>0){var n=e._queue[0];e.once(n.event,function(){e._queue.shift(),e._loadQueue()}),n.action()}return e},_ended:function(e){var o=this,t=e._sprite,r=!(!e._loop&&!o._sprite[t][2]);if(o._emit("end",e._id),!o._webAudio&&r&&o.stop(e._id,!0).play(e._id),o._webAudio&&r){o._emit("play",e._id),e._seek=e._start||0,e._rateSeek=0,e._playStart=n.ctx.currentTime;var u=1e3*(e._stop-e._start)/Math.abs(e._rate);o._endTimers[e._id]=setTimeout(o._ended.bind(o,e),u)}return o._webAudio&&!r&&(e._paused=!0,e._ended=!0,e._seek=e._start||0,e._rateSeek=0,o._clearTimer(e._id),o._cleanBuffer(e._node),n._autoSuspend()),o._webAudio||r||o.stop(e._id),o},_clearTimer:function(e){var n=this;return n._endTimers[e]&&(clearTimeout(n._endTimers[e]),delete n._endTimers[e]),n},_soundById:function(e){for(var n=this,o=0;o=0;t--){if(o<=n)return;e._sounds[t]._ended&&(e._webAudio&&e._sounds[t]._node&&e._sounds[t]._node.disconnect(0),e._sounds.splice(t,1),o--)}}},_getSoundIds:function(e){var n=this;if("undefined"==typeof e){for(var o=[],t=0;t0&&(r[o._src]=e,i(o,e))},function(){o._emit("loaderror",null,"Decoding audio data failed.")})},i=function(e,n){n&&!e._duration&&(e._duration=n.duration),0===Object.keys(e._sprite).length&&(e._sprite={__default:[0,1e3*e._duration]}),"loaded"!==e._state&&(e._state="loaded",e._emit("load"),e._loadQueue()),e._autoplay&&e.play()},_=function(){n.noAudio=!1;try{"undefined"!=typeof AudioContext?n.ctx=new AudioContext:"undefined"!=typeof webkitAudioContext?n.ctx=new webkitAudioContext:n.usingWebAudio=!1}catch(e){n.usingWebAudio=!1}if(!n.usingWebAudio)if("undefined"!=typeof Audio)try{var e=new Audio;"undefined"==typeof e.oncanplaythrough&&(n._canPlayEvent="canplay")}catch(e){n.noAudio=!0}else n.noAudio=!0;try{var e=new Audio;e.muted&&(n.noAudio=!0)}catch(e){}var o=/iP(hone|od|ad)/.test(n._navigator&&n._navigator.platform),t=n._navigator&&n._navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/),r=t?parseInt(t[1],10):null;if(o&&r&&r<9){var u=/safari/.test(n._navigator&&n._navigator.userAgent.toLowerCase());(n._navigator&&n._navigator.standalone&&!u||n._navigator&&!n._navigator.standalone&&!u)&&(n.usingWebAudio=!1)}n.usingWebAudio&&(n.masterGain="undefined"==typeof n.ctx.createGain?n.ctx.createGainNode():n.ctx.createGain(),n.masterGain.gain.value=1,n.masterGain.connect(n.ctx.destination)),n._setup()};"function"==typeof define&&define.amd&&define([],function(){return{Howler:n,Howl:o}}),"undefined"!=typeof exports&&(exports.Howler=n,exports.Howl=o),"undefined"!=typeof window?(window.HowlerGlobal=e,window.Howler=n,window.Howl=o,window.Sound=t):"undefined"!=typeof global&&(global.HowlerGlobal=e,global.Howler=n,global.Howl=o,global.Sound=t)}(); +/*! Spatial Plugin */ +!function(){"use strict";HowlerGlobal.prototype._pos=[0,0,0],HowlerGlobal.prototype._orientation=[0,0,-1,0,1,0],HowlerGlobal.prototype.stereo=function(e){var n=this;if(!n.ctx||!n.ctx.listener)return n;for(var t=n._howls.length-1;t>=0;t--)n._howls[t].stereo(e);return n},HowlerGlobal.prototype.pos=function(e,n,t){var o=this;return o.ctx&&o.ctx.listener?(n="number"!=typeof n?o._pos[1]:n,t="number"!=typeof t?o._pos[2]:t,"number"!=typeof e?o._pos:(o._pos=[e,n,t],o.ctx.listener.setPosition(o._pos[0],o._pos[1],o._pos[2]),o)):o},HowlerGlobal.prototype.orientation=function(e,n,t,o,r,i){var a=this;if(!a.ctx||!a.ctx.listener)return a;var p=a._orientation;return n="number"!=typeof n?p[1]:n,t="number"!=typeof t?p[2]:t,o="number"!=typeof o?p[3]:o,r="number"!=typeof r?p[4]:r,i="number"!=typeof i?p[5]:i,"number"!=typeof e?p:(a._orientation=[e,n,t,o,r,i],a.ctx.listener.setOrientation(e,n,t,o,r,i),a)},Howl.prototype.init=function(e){return function(n){var t=this;return t._orientation=n.orientation||[1,0,0],t._stereo=n.stereo||null,t._pos=n.pos||null,t._pannerAttr={coneInnerAngle:"undefined"!=typeof n.coneInnerAngle?n.coneInnerAngle:360,coneOuterAngle:"undefined"!=typeof n.coneOuterAngle?n.coneOuterAngle:360,coneOuterGain:"undefined"!=typeof n.coneOuterGain?n.coneOuterGain:0,distanceModel:"undefined"!=typeof n.distanceModel?n.distanceModel:"inverse",maxDistance:"undefined"!=typeof n.maxDistance?n.maxDistance:1e4,panningModel:"undefined"!=typeof n.panningModel?n.panningModel:"HRTF",refDistance:"undefined"!=typeof n.refDistance?n.refDistance:1,rolloffFactor:"undefined"!=typeof n.rolloffFactor?n.rolloffFactor:1},t._onstereo=n.onstereo?[{fn:n.onstereo}]:[],t._onpos=n.onpos?[{fn:n.onpos}]:[],t._onorientation=n.onorientation?[{fn:n.onorientation}]:[],e.call(this,n)}}(Howl.prototype.init),Howl.prototype.stereo=function(n,t){var o=this;if(!o._webAudio)return o;if("loaded"!==o._state)return o._queue.push({event:"stereo",action:function(){o.stereo(n,t)}}),o;var r="undefined"==typeof Howler.ctx.createStereoPanner?"spatial":"stereo";if("undefined"==typeof t){if("number"!=typeof n)return o._stereo;o._stereo=n,o._pos=[n,0,0]}for(var i=o._getSoundIds(t),a=0;a + + + @@ -43,11 +46,11 @@ - + diff --git a/lime/audio/AudioBuffer.hx b/lime/audio/AudioBuffer.hx index 295c46496..026e28745 100644 --- a/lime/audio/AudioBuffer.hx +++ b/lime/audio/AudioBuffer.hx @@ -3,10 +3,11 @@ package lime.audio; import haxe.io.Bytes; import lime.audio.openal.AL; -//import lime.net.URLLoader; -//import lime.net.URLRequest; import lime.utils.UInt8Array; +#if howlerjs +import lime.audio.howlerjs.Howl; +#end #if (js && html5) import js.html.Audio; #elseif flash @@ -29,16 +30,13 @@ class AudioBuffer { public var data:UInt8Array; public var id:UInt; public var sampleRate:Int; + public var src (get, set):Dynamic; - #if (js && html5) - public var src:Audio; - #elseif flash - public var src:Sound; - #elseif lime_console - public var src:FMODSound; - #else - public var src:Dynamic; - #end + @:noCompletion private var __srcAudio:#if (js && html5) Audio #else Dynamic #end; + @:noCompletion private var __srcCustom:Dynamic; + @:noCompletion private var __srcFMODSound:#if lime_console FMODSound #else Dynamic #end; + @:noCompletion private var __srcHowl:#if howlerjs Howl #else Dynamic #end; + @:noCompletion private var __srcSound:#if flash Sound #else Dynamic #end; public function new () { @@ -113,7 +111,13 @@ class AudioBuffer { if (path == null) return null; - #if lime_console + #if (js && html5 && howlerjs) + + var audioBuffer = new AudioBuffer (); + audioBuffer.__srcHowl = new Howl ({ src: [ path ] }); + return audioBuffer; + + #elseif lime_console var mode = StringTools.endsWith(path, ".wav") ? FMODMode.LOOP_OFF : FMODMode.LOOP_NORMAL; var sound:FMODSound = FMODSound.fromFile (path, mode); @@ -132,7 +136,7 @@ class AudioBuffer { audioBuffer.channels = 1; audioBuffer.data = null; audioBuffer.sampleRate = 0; - audioBuffer.src = sound; + audioBuffer.__srcFMODSound = sound; cpp.vm.Gc.setFinalizer (audioBuffer, cpp.Function.fromStaticFunction (finalize)); return audioBuffer; @@ -169,6 +173,32 @@ class AudioBuffer { } + public static function fromFiles (paths:Array):AudioBuffer { + + #if (js && html5 && howlerjs) + + var audioBuffer = new AudioBuffer (); + audioBuffer.__srcHowl = new Howl ({ src: paths }); + return audioBuffer; + + #else + + var buffer = null; + + for (path in paths) { + + buffer = AudioBuffer.fromFile (path); + if (buffer != null) break; + + } + + return buffer; + + #end + + } + + public static function fromURL (url:String, handler:AudioBuffer->Void):Void { if (url != null && url.indexOf ("http://") == -1 && url.indexOf ("https://") == -1) { @@ -209,9 +239,81 @@ class AudioBuffer { } + + + // Get & Set Methods + + + + + private function get_src ():Dynamic { + + #if (js && html5) + #if howlerjs + + return __srcHowl; + + #else + + return __srcAudio; + + #end + #elseif flash + + return __srcSound; + + #elseif lime_console + + return __srcFMODSound; + + #else + + return __srcCustom; + + #end + + } + + + private function set_src (value:Dynamic):Dynamic { + + #if (js && html5) + #if howlerjs + + return __srcHowl = value; + + #else + + return __srcAudio = value; + + #end + #elseif flash + + return __srcSound = value; + + #elseif lime_console + + return __srcFMODSound = value; + + #else + + return __srcCustom = value; + + #end + + } + + + + + // Native Methods + + + + #if (lime_cffi && !macro) @:cffi private static function lime_audio_load (data:Dynamic, buffer:Dynamic):Dynamic; #end -} +} \ No newline at end of file diff --git a/lime/audio/AudioSource.hx b/lime/audio/AudioSource.hx index de5697d74..2e9276562 100644 --- a/lime/audio/AudioSource.hx +++ b/lime/audio/AudioSource.hx @@ -24,6 +24,8 @@ void haxe_staticfunc_onFmodChannelEnd (ConsoleFmodChannel c) { ") #end +@:access(lime.audio.AudioBuffer) + class AudioSource { @@ -158,10 +160,29 @@ class AudioSource { public function play ():Void { #if html5 + #if howlerjs + + if (playing || buffer == null) { + + return; + + } + + playing = true; + + var time = currentTime; + + __completed = false; + id = buffer.__srcHowl.play (); + buffer.__srcHowl.on ("end", howl_onEnd, id); + + currentTime = time; + + #end #elseif flash if (channel != null) channel.stop (); - channel = buffer.src.play (pauseTime / 1000 + offset, loops + 1); + channel = buffer.__srcSound.play (pauseTime / 1000 + offset, loops + 1); #elseif lime_console @@ -171,7 +192,7 @@ class AudioSource { } else { - channel = buffer.src.play (); + channel = buffer.__srcSound.play (); channel.setLoopCount (__loops); if (__gain < 1.0) { channel.setVolume (__gain); @@ -211,6 +232,12 @@ class AudioSource { public function pause ():Void { #if html5 + #if howlerjs + + playing = false; + buffer.__srcHowl.pause (id); + + #end #elseif flash if (channel != null) { @@ -247,6 +274,12 @@ class AudioSource { public function stop ():Void { #if html5 + #if howlerjs + + playing = false; + buffer.__srcHowl.stop (id); + + #end #elseif flash pauseTime = 0; @@ -370,6 +403,34 @@ class AudioSource { + private function howl_onEnd () { + + #if howlerjs + + playing = false; + + if (loops > 0) { + + loops--; + stop (); + //currentTime = 0; + play (); + return; + + } else { + + buffer.__srcHowl.stop (id); + + } + + __completed = true; + onComplete.dispatch (); + + #end + + } + + private function timer_onRun () { #if (!flash && !html5) @@ -408,9 +469,23 @@ class AudioSource { private function get_currentTime ():Int { #if html5 + #if howlerjs + + if (__completed) { + + return length; + + } else { + + return Std.int (buffer.__srcHowl.seek (id) * 1000); + + } + + #else return 0; + #end #elseif flash if (channel != null) { @@ -450,9 +525,22 @@ class AudioSource { private function set_currentTime (value:Int):Int { #if html5 + #if howlerjs + + if (buffer != null) { + + //if (playing) buffer.__srcHowl.play (id); + buffer.__srcHowl.seek ((value + offset) / 1000, id); + + } + + return value; + + #else return pauseTime = value; + #end #elseif flash // TODO: create new sound channel @@ -508,9 +596,15 @@ class AudioSource { private function get_gain ():Float { #if html5 + #if howlerjs + + return buffer.__srcHowl.volume (id); + + #else return 1; + #end #elseif flash return channel.soundTransform.volume; @@ -537,9 +631,16 @@ class AudioSource { private function set_gain (value:Float):Float { #if html5 + #if howlerjs + + buffer.__srcHowl.volume (value, id); + return value; + + #else return 1; + #end #elseif flash var soundTransform = channel.soundTransform; @@ -576,12 +677,18 @@ class AudioSource { } #if html5 + #if howlerjs + + return Std.int (buffer.__srcHowl.duration () * 1000); + + #else return 0; + #end #elseif flash - return Std.int (buffer.src.length) - offset; + return Std.int (buffer.__srcSound.length) - offset; #elseif lime_console @@ -670,6 +777,11 @@ class AudioSource { private function get_position ():Vector4 { #if html5 + #if howlerjs + + // TODO: Use 3D audio plugin + + #end #elseif flash __position.x = channel.soundTransform.pan; diff --git a/lime/audio/HTML5AudioContext.hx b/lime/audio/HTML5AudioContext.hx index fab1249bf..77e261732 100644 --- a/lime/audio/HTML5AudioContext.hx +++ b/lime/audio/HTML5AudioContext.hx @@ -5,6 +5,8 @@ package lime.audio; import js.html.Audio; #end +@:access(lime.audio.AudioBuffer) + class HTML5AudioContext { @@ -30,9 +32,9 @@ class HTML5AudioContext { public function canPlayType (buffer:AudioBuffer, type:String):String { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.canPlayType (type); + return buffer.__srcAudio.canPlayType (type); } #end @@ -46,8 +48,8 @@ class HTML5AudioContext { #if (js && html5) var buffer = new AudioBuffer (); - buffer.src = new Audio (); - buffer.src.src = urlString; + buffer.__srcAudio = new Audio (); + buffer.__srcAudio.src = urlString; return buffer; #else return null; @@ -60,9 +62,9 @@ class HTML5AudioContext { public function getAudioDecodedByteCount (buffer:AudioBuffer):Int { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.audioDecodedByteCount; + return buffer.__srcAudio.audioDecodedByteCount; } #end @@ -76,9 +78,9 @@ class HTML5AudioContext { public function getAutoplay (buffer:AudioBuffer):Bool { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.autoplay; + return buffer.__srcAudio.autoplay; } #end @@ -91,9 +93,9 @@ class HTML5AudioContext { public function getBuffered (buffer:AudioBuffer):Dynamic /*TimeRanges*/ { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.buffered; + return buffer.__srcAudio.buffered; } #end @@ -107,9 +109,9 @@ class HTML5AudioContext { public function getController (buffer:AudioBuffer):Dynamic /*MediaController*/ { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.controller; + return buffer.__srcAudio.controller; } #end @@ -123,9 +125,9 @@ class HTML5AudioContext { public function getCurrentSrc (buffer:AudioBuffer):String { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.currentSrc; + return buffer.__srcAudio.currentSrc; } #end @@ -138,9 +140,9 @@ class HTML5AudioContext { public function getCurrentTime (buffer:AudioBuffer):Float { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.currentTime; + return buffer.__srcAudio.currentTime; } #end @@ -153,9 +155,9 @@ class HTML5AudioContext { public function getDefaultPlaybackRate (buffer:AudioBuffer):Float { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.defaultPlaybackRate; + return buffer.__srcAudio.defaultPlaybackRate; } #end @@ -168,9 +170,9 @@ class HTML5AudioContext { public function getDuration (buffer:AudioBuffer):Float { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.duration; + return buffer.__srcAudio.duration; } #end @@ -183,9 +185,9 @@ class HTML5AudioContext { public function getEnded (buffer:AudioBuffer):Bool { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.ended; + return buffer.__srcAudio.ended; } #end @@ -198,9 +200,9 @@ class HTML5AudioContext { public function getError (buffer:AudioBuffer):Dynamic /*MediaError*/ { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.error; + return buffer.__srcAudio.error; } #end @@ -214,9 +216,9 @@ class HTML5AudioContext { public function getInitialTime (buffer:AudioBuffer):Float { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.initialTime; + return buffer.__srcAudio.initialTime; } #end @@ -230,9 +232,9 @@ class HTML5AudioContext { public function getLoop (buffer:AudioBuffer):Bool { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.loop; + return buffer.__srcAudio.loop; } #end @@ -246,9 +248,9 @@ class HTML5AudioContext { public function getMediaGroup (buffer:AudioBuffer):String { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.mediaGroup; + return buffer.__srcAudio.mediaGroup; } #end @@ -262,9 +264,9 @@ class HTML5AudioContext { public function getMuted (buffer:AudioBuffer):Bool { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.muted; + return buffer.__srcAudio.muted; } #end @@ -277,9 +279,9 @@ class HTML5AudioContext { public function getNetworkState (buffer:AudioBuffer):Int { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.networkState; + return buffer.__srcAudio.networkState; } #end @@ -292,9 +294,9 @@ class HTML5AudioContext { public function getPaused (buffer:AudioBuffer):Bool { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.paused; + return buffer.__srcAudio.paused; } #end @@ -307,9 +309,9 @@ class HTML5AudioContext { public function getPlaybackRate (buffer:AudioBuffer):Float { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.playbackRate; + return buffer.__srcAudio.playbackRate; } #end @@ -322,9 +324,9 @@ class HTML5AudioContext { public function getPlayed (buffer:AudioBuffer):Dynamic /*TimeRanges*/ { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.played; + return buffer.__srcAudio.played; } #end @@ -337,9 +339,9 @@ class HTML5AudioContext { public function getPreload (buffer:AudioBuffer):String { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.preload; + return buffer.__srcAudio.preload; } #end @@ -352,9 +354,9 @@ class HTML5AudioContext { public function getReadyState (buffer:AudioBuffer):Int { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.readyState; + return buffer.__srcAudio.readyState; } #end @@ -367,9 +369,9 @@ class HTML5AudioContext { public function getSeekable (buffer:AudioBuffer):Dynamic /*TimeRanges*/ { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.seekable; + return buffer.__srcAudio.seekable; } #end @@ -382,9 +384,9 @@ class HTML5AudioContext { public function getSeeking (buffer:AudioBuffer):Bool { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.seeking; + return buffer.__srcAudio.seeking; } #end @@ -397,9 +399,9 @@ class HTML5AudioContext { public function getSrc (buffer:AudioBuffer):String { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.src; + return buffer.__srcAudio.src; } #end @@ -412,9 +414,9 @@ class HTML5AudioContext { public function getStartTime (buffer:AudioBuffer):Float { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.playbackRate; + return buffer.__srcAudio.playbackRate; } #end @@ -427,9 +429,9 @@ class HTML5AudioContext { public function getVolume (buffer:AudioBuffer):Float { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.volume; + return buffer.__srcAudio.volume; } #end @@ -442,9 +444,9 @@ class HTML5AudioContext { public function load (buffer:AudioBuffer):Void { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.load (); + return buffer.__srcAudio.load (); } #end @@ -455,9 +457,9 @@ class HTML5AudioContext { public function pause (buffer:AudioBuffer):Void { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.pause (); + return buffer.__srcAudio.pause (); } #end @@ -468,9 +470,9 @@ class HTML5AudioContext { public function play (buffer:AudioBuffer):Void { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - return buffer.src.play (); + return buffer.__srcAudio.play (); } #end @@ -481,9 +483,9 @@ class HTML5AudioContext { public function setAutoplay (buffer:AudioBuffer, value:Bool):Void { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - buffer.src.autoplay = value; + buffer.__srcAudio.autoplay = value; } #end @@ -495,9 +497,9 @@ class HTML5AudioContext { public function setController (buffer:AudioBuffer, value:Dynamic /*MediaController*/):Void { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - buffer.src.controller = value; + buffer.__srcAudio.controller = value; } #end @@ -509,9 +511,9 @@ class HTML5AudioContext { public function setCurrentTime (buffer:AudioBuffer, value:Float):Void { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - buffer.src.currentTime = value; + buffer.__srcAudio.currentTime = value; } #end @@ -522,9 +524,9 @@ class HTML5AudioContext { public function setDefaultPlaybackRate (buffer:AudioBuffer, value:Float):Void { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - buffer.src.defaultPlaybackRate = value; + buffer.__srcAudio.defaultPlaybackRate = value; } #end @@ -535,9 +537,9 @@ class HTML5AudioContext { public function setLoop (buffer:AudioBuffer, value:Bool):Void { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - buffer.src.loop = value; + buffer.__srcAudio.loop = value; } #end @@ -549,9 +551,9 @@ class HTML5AudioContext { public function setMediaGroup (buffer:AudioBuffer, value:String):Void { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - buffer.src.mediaGroup = value; + buffer.__srcAudio.mediaGroup = value; } #end @@ -563,9 +565,9 @@ class HTML5AudioContext { public function setMuted (buffer:AudioBuffer, value:Bool):Void { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - buffer.src.muted = value; + buffer.__srcAudio.muted = value; } #end @@ -576,9 +578,9 @@ class HTML5AudioContext { public function setPlaybackRate (buffer:AudioBuffer, value:Float):Void { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - buffer.src.playbackRate = value; + buffer.__srcAudio.playbackRate = value; } #end @@ -589,9 +591,9 @@ class HTML5AudioContext { public function setPreload (buffer:AudioBuffer, value:String):Void { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - buffer.src.preload = value; + buffer.__srcAudio.preload = value; } #end @@ -602,9 +604,9 @@ class HTML5AudioContext { public function setSrc (buffer:AudioBuffer, value:String):Void { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - buffer.src.src = value; + buffer.__srcAudio.src = value; } #end @@ -615,9 +617,9 @@ class HTML5AudioContext { public function setVolume (buffer:AudioBuffer, value:Float):Void { #if (js && html5) - if (buffer.src != null) { + if (buffer.__srcAudio != null) { - buffer.src.volume = value; + buffer.__srcAudio.volume = value; } #end diff --git a/lime/audio/howlerjs/Howl.hx b/lime/audio/howlerjs/Howl.hx new file mode 100644 index 000000000..dc98032b2 --- /dev/null +++ b/lime/audio/howlerjs/Howl.hx @@ -0,0 +1,330 @@ +package lime.audio.howlerjs; #if (!js || !html5 || display) + + +import haxe.Constraints.Function; + + +class Howl { + + + public function new (options:HowlOptions) { + + + + } + + + /** + * Get the duration of this sound. Passing a sound id will return the sprite duration. + * @param id The sound id to check. If none is passed, return full source duration. + * @return Audio duration in seconds. + */ + public function duration (?id:Int):Int { + + return 0; + + } + + + /** + * Fade a currently playing sound between two volumes (if no id is passsed, all sounds will fade). + * @param from The value to fade from (0.0 to 1.0). + * @param to The volume to fade to (0.0 to 1.0). + * @param len Time in milliseconds to fade. + * @param id The sound id (omit to fade all sounds). + * @return + */ + public function fade (from:Float, to:Float, len:Int, ?id:Int):Howl { + + return this; + + } + + + /** + * Load the audio file. + * @return + */ + public function load ():Howl { + + return this; + + } + + + /** + * Get/set the loop parameter on a sound. This method can optionally take 0, 1 or 2 arguments. + * loop() -> Returns the group's loop value. + * loop(id) -> Returns the sound id's loop value. + * loop(loop) -> Sets the loop value for all sounds in this Howl group. + * loop(loop, id) -> Sets the loop value of passed sound id. + * @return Returns self or current loop value. + */ + public function loop (?loop:Dynamic, ?id:Int):Dynamic { + + return null; + + } + + + /** + * Mute/unmute a single sound or all sounds in this Howl group. + * @param muted Set to true to mute and false to unmute. + * @param id The sound ID to update (omit to mute/unmute all). + * @return + */ + public function mute (muted:Bool, ?id:Int):Howl { + + return this; + + } + + + /** + * Remove a custom event. Call without parameters to remove all events. + * @param event Event name. + * @param fn Listener to remove. Leave empty to remove all. + * @param id (optional) Only remove events for this sound. + * @return + */ + public function off (event:String, fn:Function, ?id:Int):Howl { + + return this; + + } + + + /** + * Listen to a custom event. + * @param event Event name. + * @param fn Listener to call. + * @param id (optional) Only listen to events for this sound. + * @return + */ + public function on (event:String, fn:Function, ?id:Int):Howl { + + return this; + + } + + + /** + * Listen to a custom event and remove it once fired. + * @param event Event name. + * @param fn Listener to call. + * @param id (optional) Only listen to events for this sound. + * @return + */ + public function once (event:String, fn:Function, ?id:Int):Howl { + + return this; + + } + + + /** + * Pause playback and save current position. + * @param id The sound ID (empty to pause all in group). + * @return + */ + public function pause (?id:Int):Howl { + + return this; + + } + + + /** + * Play a sound or resume previous playback. + * @param sprite Sprite name for sprite playback or sound id to continue previous. + * @return Sound ID. + */ + public function play (?sprite:Dynamic):Int { + + return 0; + + } + + + /** + * Check if a specific sound is currently playing or not (if id is provided), or check if at least one of the sounds in the group is playing or not. + * @param id The sound id to check. If none is passed, the whole sound group is checked. + * @return True if playing and false if not. + */ + public function playing (?id:Int):Bool { + + return false; + + } + + + /** + * Get/set the playback rate of a sound. This method can optionally take 0, 1 or 2 arguments. + * rate() -> Returns the first sound node's current playback rate. + * rate(id) -> Returns the sound id's current playback rate. + * rate(rate) -> Sets the playback rate of all sounds in this Howl group. + * rate(rate, id) -> Sets the playback rate of passed sound id. + * @return Returns self or the current playback rate. + */ + public function rate (?rate:Float, ?id:Int):Dynamic { + + return null; + + } + + + /** + * Get/set the seek position of a sound (in seconds). This method can optionally take 0, 1 or 2 arguments. + * seek() -> Returns the first sound node's current seek position. + * seek(id) -> Returns the sound id's current seek position. + * seek(seek) -> Sets the seek position of the first sound node. + * seek(seek, id) -> Sets the seek position of passed sound id. + * @return Returns self or the current seek position. + */ + public function seek (?seek:Float, ?id:Int):Dynamic { + + return null; + + } + + + /** + * Returns the current loaded state of this Howl. + * @return 'unloaded', 'loading', 'loaded' + */ + public function state ():String { + + return null; + + } + + + /** + * Stop playback and reset to start. + * @param id The sound ID (empty to stop all in group). + * @return + */ + public function stop (?id:Int):Howl { + + return this; + + } + + + /** + * Unload and destroy the current Howl object. + * This will immediately stop all sound instances attached to this group. + */ + public function unload ():Void { + + + + } + + + /** + * Get/set the volume of this sound or of the Howl group. This method can optionally take 0, 1 or 2 arguments. + * volume() -> Returns the group's volume value. + * volume(id) -> Returns the sound id's current volume. + * volume(vol) -> Sets the volume of all sounds in this Howl group. + * volume(vol, id) -> Sets the volume of passed sound id. + * @return Returns self or current volume. + */ + public function volume (?vol:Float, ?id:Int):Dynamic { + + return null; + + } + + +} + + + +#else + + +import haxe.Constraints.Function; +import haxe.extern.EitherType; + +@:native("Howl") + + +extern class Howl { + + + public function new (options:HowlOptions); + + public function duration (?id:Int):Int; + public function fade (from:Float, to:Float, len:Int, ?id:Int):Howl; + public function load ():Howl; + + @:overload(function(id:Int):Bool {}) + @:overload(function(loop:Bool):Howl {}) + @:overload(function(loop:Bool, id:Int):Howl {}) + public function loop ():Bool; + + public function mute (muted:Bool, ?id:Int):Howl; + public function off (event:String, fn:Function, ?id:Int):Howl; + public function on (event:String, fn:Function, ?id:Int):Howl; + public function once (event:String, fn:Function, ?id:Int):Howl; + public function pause (?id:Int):Howl; + + @:overload(function(id:Int):Int {}) + public function play (?sprite:String):Int; + + public function playing (?id:Int):Bool; + + @:overload(function(id:Int):Float {}) + @:overload(function(rate:Float):Howl {}) + @:overload(function(rate:Float, id:Int):Howl {}) + public function rate ():Float; + + public function state ():String; + + @:overload(function(id:Int):Float {}) + @:overload(function(seek:Float):Howl {}) + @:overload(function(seek:Float, id:Int):Howl {}) + public function seek ():Float; + + public function stop (?id:Int):Howl; + public function unload ():Void; + + @:overload(function(id:Int):Float {}) + @:overload(function(vol:Float):Howl {}) + @:overload(function(vol:Float, id:Int):Howl {}) + public function volume ():Float; + + +} + + +#end + + +typedef HowlOptions = { + + src:Array, + ?volume:Float, + ?html5:Bool, + ?loop:Bool, + ?preload:Bool, + ?autoplay:Bool, + ?mute:Bool, + ?sprite:Dynamic, + ?rate:Float, + ?pool:Float, + ?format:Array, + ?onload:Function, + ?onloaderror:Function, + ?onplay:Function, + ?onend:Function, + ?onpause:Function, + ?onstop:Function, + ?onmute:Function, + ?onvolume:Function, + ?onrate:Function, + ?onseek:Function, + ?onfade:Function + +} \ No newline at end of file diff --git a/lime/audio/howlerjs/Howler.hx b/lime/audio/howlerjs/Howler.hx new file mode 100644 index 000000000..212d54897 --- /dev/null +++ b/lime/audio/howlerjs/Howler.hx @@ -0,0 +1,95 @@ +package lime.audio.howlerjs; #if (!js || !html5 || display) + + +class Howler { + + + public static var autoSuspend:Bool; + public static var ctx:WebAudioContext; + public static var masterGain:Dynamic; + public static var mobileAutoEnable:Bool; + public static var noAudio:Bool; + public static var usingWebAudio:Bool; + + + /** + * Check for codec support of specific extension. + * @param ext Audio file extention. + * @return + */ + public static function codecs (ext:String):Bool { + + return false; + + } + + + /** + * Handle muting and unmuting globally. + * @param muted Is muted or not. + */ + public static function mute (muted:Bool):Class { + + return Howler; + + } + + + /** + * Unload and destroy all currently loaded Howl objects. + * @return + */ + public static function unload ():Class { + + return Howler; + + } + + + /** + * Get/set the global volume for all sounds. + * @param vol Volume from 0.0 to 1.0. + * @return Returns self or current volume. + */ + public static function volume (?vol:Float):Dynamic { + + if (vol != null) return Howler; + return vol; + + } + + +} + + + +#else + + +import haxe.extern.EitherType; +import js.html.audio.GainNode; +import lime.audio.WebAudioContext; + +@:native("Howler") + + +extern class Howler { + + + public static var autoSuspend:Bool; + public static var ctx:WebAudioContext; + public static var masterGain:GainNode; + public static var mobileAutoEnable:Bool; + public static var noAudio:Bool; + public static var usingWebAudio:Bool; + + public static function codecs (ext:String):Bool; + public static function mute (muted:Bool):Howler; + public static function unload ():Howler; + public static function volume (?vol:Float):EitherType; + + +} + + +#end \ No newline at end of file