Merge: Extends catalog with a *try it* and a *download apk* links
[nit.git] / lib / android / audio.nit
index 7dd2767..9cc54d7 100644 (file)
 # limitations under the License.
 
 # Android audio services, wraps a part of android audio API
+# This module modifies the default behaviour of the audio loading:
+# It is first loaded from the `res/raw` folder.
+# The file extension is not needed for the `res/raw` loading part.
+# If it didn't work, it is loaded from the `assets` folder.
+# The file extension is needed for the `assets` loading part.
+#
+# `assets` contains the portable version of sounds, since the `res` folder exsists only in android projects.
 #
 # For this example, the sounds "test_sound" and "test_music" are located in the "assets/sounds" folder,
 # they both have ".ogg" extension. "test_sound" is a short sound and "test_music" a music track
@@ -41,7 +48,7 @@ module audio
 import java
 import java::io
 intrude import assets_and_resources
-import native_app_glue # FIXME update this module to use nit_activity
+import activities
 import app::audio
 
 in "Java" `{
@@ -131,7 +138,6 @@ private extern class NativeMediaPlayer in "Java" `{ android.media.MediaPlayer `}
                        self.setDataSource(fd, start_offset, length);
                        return 1;
                }catch(Exception e) {
-                       Log.e("Error loading the Media Player with a file descriptor", e.getMessage());
                        return 0;
                }
        `}
@@ -371,6 +377,16 @@ class MediaPlayer
 
        # Load a sound for a given resource id
        fun load_sound(id: Int, context: NativeActivity): Music do
+               # FIXME: maybe find a better way to handle this situation
+               # If two different music are loaded with the same `MediaPlayer`,
+               # a new `NativeMediaPlayer` will be created for the secondd music
+               # and the nit program will loose the handle to the previous one
+               # If the previous music is playing, we need to stop it
+               if playing then
+                       stop
+                       reset
+                       destroy
+               end
                self.nmedia_player = self.nmedia_player.create(context, id)
                if self.nmedia_player.is_java_null then
                        self.error = new Error("Failed to load a sound")
@@ -476,7 +492,11 @@ class MediaPlayer
 end
 
 redef class PlayableAudio
-       redef init do app.add_to_sounds(self)
+       # Flag to know if the user paused the sound
+       # Used when the app pause all sounds or resume all sounds
+       var paused: Bool = false
+
+       redef init do add_to_sounds(self)
 end
 
 redef class Sound
@@ -503,46 +523,48 @@ redef class Sound
 
        redef fun load do
                if is_loaded then return
-               var nam = app.asset_manager.open_fd(self.name)
-               if nam.is_java_null then
-                       self.error = new Error("Failed to get file descriptor for " + self.name)
-                       var retval_resources = app.default_soundpool.load_name_rid(app.resource_manager, app.native_activity, self.name)
-                       if retval_resources == -1 then
-                               self.error = new Error("Failed to load" + self.name)
+               var retval_resources = app.default_soundpool.load_name_rid(app.resource_manager, app.native_activity, self.name.strip_extension)
+               if retval_resources == -1 then
+                       self.error = new Error("failed to load" + self.name)
+                       var nam = app.asset_manager.open_fd(self.name)
+                       if nam.is_java_null then
+                               self.error = new Error("Failed to get file descriptor for " + self.name)
                        else
-                               self.soundpool_id = retval_resources
-                               self.soundpool = app.default_soundpool
-                               self.error = null
-                               self.soundpool.error = null
+                               var retval_assets = app.default_soundpool.load_asset_fd_rid(nam)
+                               if retval_assets == -1 then
+                                       self.error = new Error("Failed to load" + self.name)
+                               else
+                                       self.soundpool_id = retval_assets
+                                       self.soundpool = app.default_soundpool
+                                       self.error = null
+                                       self.soundpool.error = null
+                               end
                        end
                else
-                       var retval_assets = app.default_soundpool.load_asset_fd_rid(nam)
-                       if retval_assets == -1 then
-                               self.error = new Error("Failed to load" + self.name)
-                       else
-                               self.soundpool_id = retval_assets
-                               self.soundpool = app.default_soundpool
-                               self.error = null
-                               self.soundpool.error = null
-                       end
+                       self.soundpool_id = retval_resources
+                       self.soundpool = app.default_soundpool
+                       self.error = null
+                       self.soundpool.error = null
                end
                is_loaded = true
        end
 
        redef fun play do
-               if self.error != null then return
                if not is_loaded then load
+               if self.error != null then return
                soundpool.play(soundpool_id)
        end
 
        redef fun pause do
                if self.error != null or not self.is_loaded then return
                soundpool.pause_stream(soundpool_id)
+               paused = true
        end
 
        redef fun resume do
                if self.error != null or not self.is_loaded then return
                soundpool.resume(soundpool_id)
+               paused = false
        end
 
 end
@@ -567,52 +589,51 @@ redef class Music
 
        redef fun load do
                if is_loaded then return
-               var nam = app.asset_manager.open_fd(self.name)
-               if nam.is_java_null then
-                       self.error = new Error("Failed to get file descriptor for " + self.name)
-                       var mp_sound_resources = app.default_mediaplayer.load_sound(app.resource_manager.raw_id(self.name), app.native_activity)
-                       if mp_sound_resources.error != null then
-                               self.error = mp_sound_resources.error
+               var mp_sound_resources = app.default_mediaplayer.load_sound(app.resource_manager.raw_id(self.name.strip_extension), app.native_activity)
+               if mp_sound_resources.error != null then
+                       self.error = mp_sound_resources.error
+                       var nam = app.asset_manager.open_fd(self.name)
+                       if nam.is_java_null then
+                               self.error = new Error("Failed to get file descriptor for " + self.name)
                        else
-                               self.media_player = app.default_mediaplayer
-                               self.error = null
-                               self.media_player.error = null
+                               var mp_sound_assets = app.default_mediaplayer.data_source_fd(nam)
+                               if mp_sound_assets.error != null then
+                                       self.error = mp_sound_assets.error
+                               else
+                                       self.media_player = app.default_mediaplayer
+                                       self.error = null
+                                       self.media_player.error = null
+                               end
                        end
                else
-                       var mp_sound_assets = app.default_mediaplayer.data_source_fd(nam)
-                       if mp_sound_assets.error != null then
-                               self.error = mp_sound_assets.error
-                       else
-                               self.media_player = app.default_mediaplayer
-                               self.error = null
-                               self.media_player.error = null
-                       end
+                       self.media_player = app.default_mediaplayer
+                       self.error = null
+                       self.media_player.error = null
                end
                is_loaded = true
        end
 
        redef fun play do
-               if self.error != null then return
                if not is_loaded then load
+               if self.error != null then return
                media_player.start
        end
 
        redef fun pause do
                if self.error != null or not self.is_loaded then return
                media_player.pause
+               paused = true
        end
 
        redef fun resume do
                if self.error != null or not self.is_loaded then return
                play
+               paused = false
        end
 end
 
 redef class App
 
-       # Sounds handled by the application, when you load a sound, it's added to this list.
-       # This array is used in `pause` and `resume`
-       private var sounds = new Array[PlayableAudio]
 
        # Returns the default MediaPlayer of the application.
        # When you load a music, it goes in this MediaPlayer.
@@ -631,7 +652,7 @@ redef class App
 
        # Sets the stream of the app to STREAM_MUSIC.
        # STREAM_MUSIC is the default stream used by android apps.
-       private fun manage_audio_stream import native_activity, native_app_glue in "Java" `{
+       private fun manage_audio_stream import native_activity in "Java" `{
                App_native_activity(self).setVolumeControlStream(AudioManager.STREAM_MUSIC);
        `}
 
@@ -649,7 +670,7 @@ redef class App
 
        # Retrieves a music with a media player in the `assets` folder using its name.
        # Used to play long sounds or musics, can't play multiple sounds simultaneously
-       redef fun load_music(path: String): Music do
+       redef fun load_music(path) do
                var fd = asset_manager.open_fd(path)
                if not fd.is_java_null then
                        return add_to_sounds(default_mediaplayer.data_source_fd(fd)).as(Music)
@@ -669,27 +690,46 @@ redef class App
                return add_to_sounds(default_mediaplayer.load_sound(resource_manager.raw_id(music), self.native_activity)).as(Music)
        end
 
-       # Factorizes `sounds.add` to use it in `load_music`, `load_sound`, `load_music_from_res` and `load_sound_from_res`
-       private fun add_to_sounds(sound: PlayableAudio): PlayableAudio do
-               sounds.add(sound)
-               return sound
-       end
-
-       redef fun pause do
+       redef fun on_pause do
                super
-               for s in sounds do s.pause
+               for s in sounds do
+                       # Pausing sounds that are not already paused by user
+                       # `s.paused` is set to false because `pause` set it to true
+                       # and we want to know which sound has been paused by the user
+                       # and which one has been paused by the app
+                       if not s.paused then
+                               s.pause
+                               s.paused = false
+                       end
+               end
                audio_manager.abandon_audio_focus
        end
 
-       redef fun init_window do
+       redef fun on_create do
                super
                audio_manager.request_audio_focus
                manage_audio_stream
        end
 
-       redef fun resume do
+       redef fun on_resume do
                super
                audio_manager.request_audio_focus
-               for s in sounds do s.resume
+               for s in sounds do
+                       # Resumes only the sounds paused by the App
+                       if not s.paused then s.resume
+               end
+       end
+end
+
+redef class Sys
+
+       # Sounds handled by the application, when you load a sound, it's added to this list.
+       # This array is used in `pause` and `resume`
+       private var sounds = new Array[PlayableAudio]
+
+       # Factorizes `sounds.add` to use it in `load_music`, `load_sound`, `load_music_from_res` and `load_sound_from_res`
+       private fun add_to_sounds(sound: PlayableAudio): PlayableAudio do
+               sounds.add(sound)
+               return sound
        end
 end