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