diff --git a/examples/SimpleAudioExample/Assets/.keep b/examples/SimpleAudioExample/Assets/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/examples/SimpleAudioExample/Assets/ambience.ogg b/examples/SimpleAudioExample/Assets/ambience.ogg new file mode 100644 index 000000000..58b630a9f Binary files /dev/null and b/examples/SimpleAudioExample/Assets/ambience.ogg differ diff --git a/examples/SimpleAudioExample/Assets/sound.ogg b/examples/SimpleAudioExample/Assets/sound.ogg new file mode 100644 index 000000000..3aa2730c8 Binary files /dev/null and b/examples/SimpleAudioExample/Assets/sound.ogg differ diff --git a/examples/SimpleAudioExample/Assets/sound.wav b/examples/SimpleAudioExample/Assets/sound.wav new file mode 100644 index 000000000..2544fe226 Binary files /dev/null and b/examples/SimpleAudioExample/Assets/sound.wav differ diff --git a/examples/SimpleAudioExample/Assets/sound_licenses.txt b/examples/SimpleAudioExample/Assets/sound_licenses.txt new file mode 100644 index 000000000..0cb72a80b --- /dev/null +++ b/examples/SimpleAudioExample/Assets/sound_licenses.txt @@ -0,0 +1,13 @@ +sound.ogg / sound.wav + + http://www.freesound.org/people/FreqMan/sounds/42899/ + + The files are under the following license : + This work is licensed under the Attribution License. + + See the above link for more details. + + +ambience.ogg + + https://soundcloud.com/underscorediscovery/menuambience \ No newline at end of file diff --git a/examples/SimpleAudioExample/project.lime.xml b/examples/SimpleAudioExample/project.lime.xml new file mode 100644 index 000000000..ec91cd58e --- /dev/null +++ b/examples/SimpleAudioExample/project.lime.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/SimpleAudioExample/src/Main.hx b/examples/SimpleAudioExample/src/Main.hx new file mode 100644 index 000000000..7e2fad197 --- /dev/null +++ b/examples/SimpleAudioExample/src/Main.hx @@ -0,0 +1,107 @@ + + //Ported and modified from OpenFL samples + //underscorediscovery + +import lime.AudioHandler.Sound; +import lime.utils.Assets; +import lime.Lime; + + //Import GL stuff from lime +import lime.gl.GL; + +//Press any key to reset the music +//Click to play a sound + +class Main { + + public var lib : Lime; + + //Some value to mess with the clear color + private var red_value : Float = 1.0; + private var red_direction : Int = 1; + private var dt : Float = 0.016; + private var end_dt : Float = 0; + + public function new() { } + + public function ready( _lime : Lime ) { + + //Store a reference + lib = _lime; + + // Init the shaders and view + init(); + + } //ready + + + public function init() { + + lib.audio.create('music', 'assets/ambience.ogg', true ); + lib.audio.create('sound', 'assets/sound.ogg', false); + lib.audio.create('sound_wav', 'assets/sound.wav', false); + + lib.audio.get('music').play(5); + lib.audio.get('music').on_complete(function(sound:Sound){ + trace("music complete!"); + }); + + } //init + + //Called each frame by lime for logic (called before render) + public function update() { + + dt = haxe.Timer.stamp() - end_dt; + end_dt = haxe.Timer.stamp(); + + //an awful magic number to change the value slowly + red_value += (red_direction * 0.3) * dt; + + if(red_value >= 1) { + red_value = 1; + red_direction = -red_direction; + } else if(red_value <= 0) { + red_value = 0; + red_direction = -red_direction; + } + + } //update + + //Called by lime + public function onmousemove(_event:Dynamic) { + } + //Called by lime + public function onmousedown(_event:Dynamic) { + lib.audio.get('sound').play(3,0); + } + //Called by lime + public function onmouseup(_event:Dynamic) { + } + //Called by lime + public function onkeydown(_event:Dynamic) { + lib.audio.get('music').position = 0.0; + } + //Called by lime + public function onkeyup(_event:Dynamic) { + } + + + //Called by lime + public function render() { + + //Set the viewport for GL + GL.viewport( 0, 0, lib.config.width, lib.config.height ); + + //Set the clear color to a weird color that bounces around + GL.clearColor( red_value, red_value*0.5, red_value*0.3, 1); + //Clear the buffers + GL.clear( GL.COLOR_BUFFER_BIT | GL.DEPTH_BUFFER_BIT ); + + + + } //render + + +} //Main + + diff --git a/lime/AudioHandler.hx b/lime/AudioHandler.hx index 5421af947..f183795b1 100644 --- a/lime/AudioHandler.hx +++ b/lime/AudioHandler.hx @@ -3,7 +3,272 @@ package lime; import lime.utils.Libs; +#if (!audio_thread_disabled && lime_native) + #if neko + import neko.vm.Thread; + import neko.vm.Mutex; + #else + import cpp.vm.Thread; + import cpp.vm.Mutex; + #end +#end //audio_thread_disabled + + +class AudioHandler { + + public var lib : Lime; + public function new( _lib:Lime ) { lib = _lib; } + +#if (!audio_thread_disabled && lime_native) + + @:noCompletion public static var audio_state:AudioThreadState; + + @:noCompletion public static var audio_message_check_complete = 1; + @:noCompletion public static var audio_thread_is_idle:Bool = true; + @:noCompletion public static var audio_thread_running:Bool = false; + +#end + + public var sounds : Map; + public function startup() { + + sounds = new Map(); + + #if (!audio_thread_disabled && lime_native) + audio_state = new AudioThreadState (); + audio_thread_running = true; + audio_thread_is_idle = false; + audio_state.main_thread = Thread.current (); + audio_state.audio_thread = Thread.create( audio_thread_handler ); + #end //#(!audio_thread_disabled && lime_native) + + } //startup + + @:noCompletion public function update() { + for(sound in sounds) { + if(sound.ismusic) { + sound.check_complete(); + } + } + } + + public function create(_name:String, _file:String, ?_music:Bool = false ) { + + if(sounds.exists(_name)) { + throw ">>> Named sounds are not allowed to have duplicate names"; + } + + #if lime_native + + var _handle = lime_sound_from_file( lime.AssetData.path.get(_file), _music ); + var _sound = new Sound(_name, _handle, _music); + + sounds.set(_name, _sound); + + #end //lime_native + } + + public function get( _name:String ) : Sound { + return sounds.get(_name); + } + +#if (!audio_thread_disabled && lime_native) + + public function audio_thread_handler() { + + while (audio_thread_running) { + + var thread_message:Dynamic = Thread.readMessage (false); + + if (thread_message == audio_message_check_complete) { + audio_state.check(); + } + + if (!audio_thread_is_idle) { + audio_state.update(); + Sys.sleep (0.01); + } else { + Sys.sleep (0.2); + } + + } + + audio_thread_running = false; + audio_thread_is_idle = true; + + } + +#end //(!audio_thread_disabled && lime_native) + + +#if lime_native + private static var lime_sound_from_file = Libs.load("lime","lime_sound_from_file", 2); + private static var lime_sound_from_data = Libs.load("lime","lime_sound_from_data", 3); +#end //lime_native + +} //AudioHandler + + +class Sound { + + public var name : String; + public var handle : Dynamic; + public var channel : Dynamic; + public var ismusic : Bool = false; + + var on_complete_handler : Sound->Void; + + @:isVar public var volume(default, set) : Float = 1.0; + @:isVar public var pan(default, set) : Float = 0.0; + @:isVar public var position(get, set) : Float = 0.0; + +#if (!audio_thread_disabled && lime_native) + @:noCompletion private var added_to_thread:Bool; +#end + + private var _transform:SoundTransform; + + public function new(_name:String, _handle:Dynamic, ?_music:Bool = false, ?_sound:Dynamic = null) { + + name = _name; + handle = _handle; + channel = _sound; + ismusic = _music; + + //reuse. + _transform = new SoundTransform(volume,pan); + } + + public function play(?_loops:Int = 0, ?_start:Float = 0.0) { + + channel = null; + + #if lime_native + channel = lime_sound_channel_create(handle, _start, _loops, _transform); + #end //lime_native + } + + public function stop() { + + if(channel != null) { + + #if (!audio_thread_disabled && lime_native) + if( ismusic ) { + AudioHandler.audio_state.remove(this); + } + #end + + #if lime_native + lime_sound_channel_stop(channel); + #end //lime_native + + channel = null; + } + } + + @:noCompletion public function do_on_complete() { + if(on_complete_handler != null) { + on_complete_handler(this); + } + } + + public function on_complete(_function:Sound->Void) { + on_complete_handler = _function; + } + + + @:noCompletion public function do_check_complete ():Bool { + + if( lime_sound_channel_is_complete(channel) ) { + handle = null; + channel = null; + return true; + } + + return false; + + } //do_check_complete + + @:noCompletion public function check_complete() { + + #if (!audio_thread_disabled && lime_native) + + if (added_to_thread || (channel != null && ismusic)) { + + if (!added_to_thread) { + trace('adding to a thread ' + name); + AudioHandler.audio_state.add( this ); + added_to_thread = true; + } + + AudioHandler.audio_state.audio_thread.sendMessage( AudioHandler.audio_message_check_complete ); + + return false; + } + + #else + + if( do_check_complete() ) { + do_on_complete(); + return true; + } + + #end + + return false; + } + + function set_volume(_v:Float) : Float { + _transform.volume = _v; + + #if lime_native + lime_sound_channel_set_transform(channel,_transform); + #end //lime_native + + return volume = _v; + } + function set_pan(_p:Float) : Float { + _transform.pan = _p; + + #if lime_native + lime_sound_channel_set_transform(channel,_transform); + #end //lime_native + + return pan = _p; + } + + function set_position(_p:Float) : Float { + + #if lime_native + lime_sound_channel_set_position(channel, _p); + #end //lime_native + + return position = _p; + } + + function get_position() : Float { + + #if lime_native + position = lime_sound_channel_get_position(channel); + #end //lime_native + + return position; + } + +#if lime_native + private static var lime_sound_channel_is_complete = Libs.load ("lime", "lime_sound_channel_is_complete", 1); + private static var lime_sound_channel_create = Libs.load("lime","lime_sound_channel_create", 4); + private static var lime_sound_channel_stop = Libs.load("lime","lime_sound_channel_stop", 1); + private static var lime_sound_channel_set_transform = Libs.load("lime","lime_sound_channel_set_transform", 2); + private static var lime_sound_channel_get_position = Libs.load ("lime", "lime_sound_channel_get_position", 1); + private static var lime_sound_channel_set_position = Libs.load ("lime", "lime_sound_channel_set_position", 2); +#end //lime_native + +} + + class SoundTransform { + public var pan:Float; public var volume:Float; @@ -15,98 +280,83 @@ class SoundTransform { public function clone() { return new SoundTransform(volume, pan); } -} -class Sound { - public var handle : Dynamic; - public var sound : Dynamic; +} //SoundTransform - @:isVar public var volume(default, set) : Float = 1.0; - @:isVar public var pan(default, set) : Float = 0.0; - private var _transform:SoundTransform; - public function new(_handle:Dynamic, ?_sound:Dynamic = null){ - handle = _handle; - sound = _sound; - //reuse. - _transform = new SoundTransform(volume,pan); - } +#if (!audio_thread_disabled && lime_native) - public function play(?_loops:Int = 0, ?_start:Float = 0.0) { - sound = null; - - #if lime_native - sound = lime_sound_channel_create(handle, _start, _loops, _transform); - #end //lime_native - } +class AudioThreadState { + + public var audio_thread:Thread; + public var sound_list:Map ; + public var main_thread:Thread; + public var mutex:Mutex; + + public function new () { + mutex = new Mutex (); + sound_list = new Map (); + } + + public function add (sound:Sound):Void { + + mutex.acquire (); + + if (!sound_list.exists(sound)) { + sound_list.set (sound, false); + AudioHandler.audio_thread_is_idle = false; + } + + mutex.release (); + + } + + public function check() { + + for (sound in sound_list.keys()) { - public function stop() { - if(sound != null) { + var is_complete = sound_list.get(sound); + if (is_complete) { + sound.do_on_complete(); + + mutex.acquire (); + sound_list.remove( sound ); + mutex.release (); + + } //isComplete + } + + } + + public function remove( sound:Sound ):Void { + + mutex.acquire (); + + if( sound_list.exists(sound) ) { + sound_list.remove(sound); + if (Lambda.count (sound_list) == 0) { + AudioHandler.audio_thread_is_idle = true; + } + } + + mutex.release (); + + } + + public function update() { + + mutex.acquire (); + + for (sound in sound_list.keys()) { + var is_complete = sound.do_check_complete(); + sound_list.set( sound, is_complete ); + } + + mutex.release (); + + } + +} //AudioThreadState - #if lime_native - lime_sound_channel_stop(sound); - #end //lime_native - - sound = null; - } - } - - public function set_volume(_v:Float) : Float { - _transform.volume = _v; - - #if lime_native - lime_sound_channel_set_transform(sound,_transform); - #end //lime_native - - return volume = _v; - } - public function set_pan(_p:Float) : Float { - _transform.pan = _p; - - #if lime_native - lime_sound_channel_set_transform(sound,_transform); - #end //lime_native - - return pan = _p; - } - -#if lime_native - private static var lime_sound_channel_create = Libs.load("lime","lime_sound_channel_create", 4); - private static var lime_sound_channel_stop = Libs.load("lime","lime_sound_channel_stop", 1); - private static var lime_sound_channel_set_transform = Libs.load("lime","lime_sound_channel_set_transform", 2); -#end //lime_native - -} - -class AudioHandler { - - public var lib : Lime; - public function new( _lib:Lime ) { lib = _lib; } - - public var sounds : Map; - public function startup() { - //test audio junks - - sounds = new Map(); - } - - public function create_sound(_name:String, _file:String, ?_music:Bool = false ) { - - if(sounds.exists(_name)) { - throw ">>> Named sounds are not allowed to have duplicate names"; - } - - #if lime_native - var _handle = lime_sound_from_file( lime.AssetData.path.get(_file), _music); - var _sound = new Sound(_handle); - sounds.set(_name, _sound); - #end //lime_native - } - -#if lime_native - private static var lime_sound_from_file = Libs.load("lime","lime_sound_from_file", 2); - private static var lime_sound_from_data = Libs.load("lime","lime_sound_from_data", 3); -#end //lime_native - -} \ No newline at end of file +#end // (!audio_thread_disabled && lime_native) \ No newline at end of file diff --git a/lime/Lime.hx b/lime/Lime.hx index db07b51c4..98c8e6a56 100644 --- a/lime/Lime.hx +++ b/lime/Lime.hx @@ -307,9 +307,12 @@ class Lime { _debug('on_update ' + Timer.stamp(), true, false); #if lime_native - Timer.__checkTimers(); + Timer.__checkTimers(); #end + //process any audio + audio.update(); + if(!has_shutdown) { #if lime_native