Merge: doc: fixed some typos and other misc. corrections
[nit.git] / lib / android / audio.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2014 Romain Chanoir <romain.chanoir@viacesi.fr>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Android audio services, wraps a part of android audio API
18 # This module modifies the default behaviour of the audio loading:
19 # It is first loaded from the `res/raw` folder.
20 # The file extension is not needed for the `res/raw` loading part.
21 # If it didn't work, it is loaded from the `assets` folder.
22 # The file extension is needed for the `assets` loading part.
23 #
24 # `assets` contains the portable version of sounds, since the `res` folder exsists only in android projects.
25 #
26 # For this example, the sounds "test_sound" and "test_music" are located in the "assets/sounds" folder,
27 # they both have ".ogg" extension. "test_sound" is a short sound and "test_music" a music track
28 #
29 # ~~~nitish
30 # # Note that you need to specify the path from "assets" folder and the extension
31 # var s = new Sound("sounds/test_sound.ogg")
32 # var m = new Music("sounds/test_music.ogg")
33 # s.play
34 # m.play
35 # ~~~
36 #
37 # Now, the sounds are in "res/raw"
38 # ~~~nitish
39 # s = app.load_sound_from_res("test_sound")
40 # m = app.load_music_from_res("test_sound")
41 # s.play
42 # m.play
43 # ~~~
44 #
45 # See http://developer.android.com/reference/android/media/package-summary.html for more infos
46 module audio
47
48 import java
49 import java::io
50 intrude import assets_and_resources
51 import activities
52 import app::audio
53
54 in "Java" `{
55 import android.media.MediaPlayer;
56 import android.media.SoundPool;
57 import java.io.IOException;
58 import android.media.AudioManager;
59 import android.media.AudioManager.OnAudioFocusChangeListener;
60 import android.content.Context;
61 import android.util.Log;
62 `}
63
64 # FIXME: This listener is not working at the moment, but is needed to gain or give up the audio focus
65 # of the application
66 in "Java inner" `{
67 static OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
68 public void onAudioFocusChange(int focusChange) {
69 if(focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
70 }else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
71 }else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
72 }
73 }
74 };
75 `}
76
77 # AudioManager of the application, used to manage the audio mode
78 private extern class NativeAudioManager in "Java" `{ android.media.AudioManager `}
79 super JavaObject
80
81 # Current audio mode.
82 # ( MODE_NORMAL = 0, MODE_RINGTONE = 1, MODE_IN_CALL = 2 or MODE_IN_COMMUNICATION = 3 )
83 fun mode: Int in "Java" `{ return self.getMode(); `}
84
85 # Sets the audio mode.
86 # ( MODE_NORMAL = 0, MODE_RINGTONE = 1, MODE_IN_CALL = 2 or MODE_IN_COMMUNICATION = 3 )
87 fun mode=(i: Int) in "Java" `{ self.setMode((int)i); `}
88
89 # Sends a request to obtain audio focus
90 fun request_audio_focus: Int in "Java" `{
91 return self.requestAudioFocus(afChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
92 `}
93
94 # Gives up audio focus
95 fun abandon_audio_focus: Int in "Java" `{ return self.abandonAudioFocus(afChangeListener); `}
96 end
97
98 # Media Player from Java, used to play long sounds or musics, not simultaneously
99 # This is a low-level class, use `MediaPlater` instead
100 private extern class NativeMediaPlayer in "Java" `{ android.media.MediaPlayer `}
101 super JavaObject
102
103 new in "Java" `{
104 MediaPlayer mp = new MediaPlayer();
105 mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
106 return mp;
107 `}
108 fun start in "Java" `{ self.start(); `}
109 fun prepare in "Java" `{
110 try {
111 self.prepare();
112 }catch(Exception e) {
113 Log.e("Error preparing the Media Player", e.getMessage());
114 e.printStackTrace();
115 }
116 `}
117
118 new create(context: NativeActivity, id: Int): NativeMediaPlayer
119 in "Java" `{
120 try {
121 return MediaPlayer.create(context, (int)id);
122 }catch(Exception e) {
123 return null;
124 }
125 `}
126
127 fun pause in "Java" `{ self.pause(); `}
128 fun stop in "Java" `{ self.stop(); `}
129 fun playing: Bool in "Java" `{ return self.isPlaying(); `}
130 fun release in "Java" `{ self.release(); `}
131 fun duration: Int in "Java" `{ return self.getDuration(); `}
132 fun looping: Bool in "Java" `{ return self.isLooping(); `}
133 fun looping=(b: Bool) in "Java" `{ self.setLooping(b); `}
134 fun volume=(vol: Float) in "Java" `{ self.setVolume((float)vol, (float)vol); `}
135 fun both_volume(left_volume, right_volume: Float) in "Java" `{ self.setVolume((float)left_volume, (float)right_volume); `}
136 fun stream_type=(stream_type: Int) in "Java" `{ self.setAudioStreamType((int)stream_type); `}
137 fun data_source_fd(fd: NativeFileDescriptor, start_offset, length: Int): Int in "Java" `{
138 try {
139 self.setDataSource(fd, start_offset, length);
140 return 1;
141 }catch(Exception e) {
142 return 0;
143 }
144 `}
145 fun data_source_path(path: JavaString): Int in "Java" `{
146 try {
147 self.setDataSource(path);
148 return 1;
149 }catch(Exception e) {
150 Log.e("Error loading the Media Player", e.getMessage());
151 return 0;
152 }
153 `}
154 fun reset in "Java" `{ self.reset(); `}
155
156 # HACK for bug #845
157 redef fun new_global_ref import sys, Sys.jni_env `{
158 Sys sys = NativeMediaPlayer_sys(self);
159 JNIEnv *env = Sys_jni_env(sys);
160 return (*env)->NewGlobalRef(env, self);
161 `}
162 end
163
164 # Sound Pool from Java, used to play sounds simultaneously
165 # This is a low-level class, use `SoundPool`instead
166 private extern class NativeSoundPool in "Java" `{ android.media.SoundPool `}
167 super JavaObject
168
169 new(max_streams, stream_type, src_quality: Int) in "Java" `{
170 return new SoundPool((int)max_streams, (int)stream_type, (int)src_quality);
171 `}
172 fun load_asset_fd(afd: NativeAssetFileDescriptor, priority: Int): Int in "Java" `{
173 try {
174 int id = self.load(afd, (int)priority);
175 return id;
176 }catch(Exception e){
177 return -1;
178 }
179 `}
180
181 fun load_id(context: NativeActivity, resid, priority: Int): Int in "Java" `{
182 try {
183 int id = self.load(context, (int)resid, (int)priority);
184 return id;
185 }catch(Exception e){
186 return -1;
187 }
188 `}
189
190 fun load_path(path: JavaString, priority: Int): Int in "Java" `{
191 try {
192 int id = self.load(path, (int)priority);
193 return id;
194 }catch(Exception e){
195 return -1;
196 }
197 `}
198
199 fun play(sound_id: Int, left_volume, right_volume: Float, priority, l: Int, rate: Float): Int in "Java" `{
200 return self.play((int)sound_id, (float)left_volume, (float)right_volume, (int)priority, (int)l, (float)rate);
201 `}
202 fun pause(stream_id: Int) in "Java" `{ self.pause((int)stream_id); `}
203 fun auto_pause in "Java" `{ self.autoPause(); `}
204 fun auto_resume in "Java" `{ self.autoResume(); `}
205 fun resume(stream_id: Int) in "Java" `{ self.resume((int)stream_id); `}
206 fun set_loop(stream_id, l: Int) in "Java" `{ self.setLoop((int)stream_id, (int)l); `}
207 fun set_priority(stream_id, priority: Int) in "Java" `{ self.setPriority((int)stream_id, (int)priority); `}
208 fun set_rate(stream_id: Int, rate: Float) in "Java" `{ self.setRate((int)stream_id, (float)rate); `}
209 fun set_volume(stream_id: Int, left_volume, right_volume: Float) in "Java" `{ self.setVolume((int)stream_id, (float)left_volume, (float)right_volume); `}
210 fun stop(stream_id: Int) in "Java" `{ self.stop((int)stream_id); `}
211 fun unload(sound_id: Int): Bool in "Java" `{ return self.unload((int)sound_id); `}
212 fun release in "Java" `{ self.release(); `}
213
214 # HACK for bug #845
215 redef fun new_global_ref import sys, Sys.jni_env `{
216 Sys sys = NativeSoundPool_sys(self);
217 JNIEnv *env = Sys_jni_env(sys);
218 return (*env)->NewGlobalRef(env, self);
219 `}
220 end
221
222
223 # Used to play sound, best suited for sounds effects in apps or games
224 class SoundPool
225
226 # Latest error on this sound pool
227 var error: nullable Error = null
228
229 private var nsoundpool: NativeSoundPool is noinit
230
231 # The maximum number of simultaneous streams for this SoundPool
232 var max_streams = 10 is writable
233
234 # The audio stream type, 3 is STREAM_MUSIC, default for game application
235 var stream_type = 3 is writable
236
237 # The sample-rate converter quality, currently has no effect
238 var src_quality = 0 is writable
239
240 # Left volume value, range 0.0 to 1.0
241 var left_volume = 1.0 is writable
242
243 # Right volume value, range 0.0 to 1.0
244 var right_volume = 1.0 is writable
245
246 # Playback rate, 1.0 = normal playback, range 0.5 to 2.0
247 var rate = 1.0 is writable
248
249 # Loop mode, 0 = no loop, -1 = loop forever
250 var looping = 0 is writable
251
252 # Stream priority
253 private var priority = 1
254
255 init do self.nsoundpool = (new NativeSoundPool(max_streams, stream_type, src_quality)).new_global_ref
256
257 # Load the sound from an asset file descriptor
258 # this function is for advanced use
259 private fun load_asset_fd(afd: NativeAssetFileDescriptor): Sound do
260 var resval = nsoundpool.load_asset_fd(afd, self.priority)
261 if resval == -1 then
262 self.error = new Error("Unable to load sound from assets")
263 return new Sound.priv_init(null, -1, self, self.error)
264 else
265 return new Sound.priv_init(null, resval, self, null)
266 end
267 end
268
269 # Returns only the id corresponding to the soundpool where the sound is loaded.
270 # Needed by `load` of `Sound`.
271 private fun load_asset_fd_rid(afd: NativeAssetFileDescriptor): Int do
272 return nsoundpool.load_asset_fd(afd, self.priority)
273 end
274
275 # Load the sound from its resource id
276 fun load_id(context: NativeActivity, id:Int): Sound do
277 var resval = nsoundpool.load_id(context, id, priority)
278 if resval == -1 then
279 self.error = new Error("Unable to load sound from assets")
280 return new Sound.priv_init(null, -1, self, self.error)
281 else
282 return new Sound.priv_init(null, resval, self, null)
283 end
284 end
285
286 # Returns only the id corresponding to the soundpool where the sound is loaded.
287 private fun load_id_rid(context: NativeActivity, id: Int): Int do
288 return nsoundpool.load_id(context, id, priority)
289 end
290
291 # Load the sound from the specified path
292 fun load_path(path: String): Sound do
293 sys.jni_env.push_local_frame(1)
294 var resval = nsoundpool.load_path(path.to_java_string, priority)
295 sys.jni_env.pop_local_frame
296 if resval == -1 then
297 self.error = new Error("Unable to load sound from path: " + path)
298 return new Sound.priv_init(null, -1, self, self.error)
299 else
300 return new Sound.priv_init(null, resval, self, null)
301 end
302 end
303
304 # Play a sound from a sound ID
305 # return non-zero streamID if successful, zero if failed
306 fun play(id: Int): Int do
307 return nsoundpool.play(id, left_volume, right_volume, priority, looping, rate)
308 end
309
310 # Load a sound by its `name` in the resources, the sound must be in the `res/raw` folder
311 fun load_name(resource_manager: ResourcesManager, context: NativeActivity, name: String): Sound do
312 var id = resource_manager.raw_id(name)
313 var resval = nsoundpool.load_id(context, id, priority)
314 if resval == -1 then
315 self.error = new Error("Unable to load sound from resources: " + name)
316 return new Sound.priv_init(null, -1, self, self.error)
317 else
318 return new Sound.priv_init(null, resval, self, null)
319 end
320 end
321
322 # Returns only the id corresponding to the soundpool where the sound is loaded.
323 private fun load_name_rid(resource_manager: ResourcesManager, context: NativeActivity, sound: String): Int do
324 var id = resource_manager.raw_id(sound)
325 return nsoundpool.load_id(context, id, priority)
326 end
327
328 # Pause a playback stream
329 fun pause_stream(stream_id: Int) do nsoundpool.pause(stream_id)
330
331 # Pause all active_streams
332 fun auto_pause do nsoundpool.auto_pause
333
334 # Resume all previously active streams
335 fun auto_resume do nsoundpool.auto_resume
336
337 # Resume a playback stream
338 fun resume(stream_id: Int) do nsoundpool.resume(stream_id)
339
340 # Set loop mode on a stream
341 fun stream_loop=(stream_id, looping: Int) do nsoundpool.set_loop(stream_id, looping)
342
343 # Change stream priority
344 fun stream_priority=(stream_id, priority: Int) do nsoundpool.set_priority(stream_id, priority)
345
346 # Change playback rate
347 fun stream_rate=(stream_id: Int, rate: Float) do nsoundpool.set_rate(stream_id, rate)
348
349 # Set stream volume
350 fun stream_volume(stream_id: Int, left_volume, right_volume: Float) do
351 nsoundpool.set_volume(stream_id, left_volume, right_volume)
352 end
353
354 # Stop a playback stream
355 fun stop_stream(stream_id: Int) do nsoundpool.stop(stream_id)
356
357 # Unload a sound from a sound ID
358 fun unload(sound: Sound): Bool do return nsoundpool.unload(sound.soundpool_id)
359
360 # Destroys the object
361 fun destroy do nsoundpool.release
362 end
363
364 # Used to play sounds, designed to use with medium sized sounds or streams
365 # The Android MediaPlayer has a complex state diagram that you'll need to
366 # respect if you want your MediaPlayer to work fine, see the android doc
367 class MediaPlayer
368 private var nmedia_player: NativeMediaPlayer is noinit
369
370 # Used to control the state of the mediaplayer
371 private var is_prepared = false is writable
372
373 # The sound associated with this mediaplayer
374 var sound: nullable Music = null is writable
375
376 # Error gestion
377 var error: nullable Error = null
378
379 # Create a new MediaPlayer, but no sound is attached, you'll need
380 # to use `load_sound` before using it
381 init do self.nmedia_player = (new NativeMediaPlayer).new_global_ref
382
383 # Init the mediaplayer with a sound resource id
384 init from_id(context: NativeActivity, id: Int) do
385 self.nmedia_player = new NativeMediaPlayer.create(context, id)
386 if self.nmedia_player.is_java_null then
387 self.error = new Error("Failed to create the MediaPlayer")
388 self.sound = new Music.priv_init(id, self, self.error)
389 end
390 self.sound = new Music.priv_init(id, self, null)
391 end
392
393 # Load a sound for a given resource id
394 fun load_sound(id: Int, context: NativeActivity): Music do
395 # FIXME: maybe find a better way to handle this situation
396 # If two different music are loaded with the same `MediaPlayer`,
397 # a new `NativeMediaPlayer` will be created for the secondd music
398 # and the nit program will loose the handle to the previous one
399 # If the previous music is playing, we need to stop it
400 if playing then
401 stop
402 reset
403 destroy
404 end
405
406 self.nmedia_player = new NativeMediaPlayer.create(context, id)
407 if self.nmedia_player.is_java_null then
408 self.error = new Error("Failed to load a sound")
409 self.sound = new Music.priv_init(id, self, new Error("Sound loading failed"))
410 return self.sound.as(not null)
411 else
412 if self.error != null then self.error = null
413 self.sound = new Music.priv_init(id, self, null)
414 self.is_prepared = true
415 return self.sound.as(not null)
416 end
417 end
418
419 # Starts or resumes playback
420 # REQUIRE `self.sound != null`
421 fun start do
422 if self.error != null then return
423 if not is_prepared then prepare
424 nmedia_player.start
425 end
426
427 # Stops playback after playback has been stopped or paused
428 # REQUIRE `self.sound != null`
429 fun stop do
430 if self.error != null then return
431 is_prepared = false
432 nmedia_player.stop
433 end
434
435 # Prepares the player for playback, synchronously
436 # REQUIRE `self.sound != null`
437 fun prepare do
438 if self.error != null then return
439 assert sound != null
440 nmedia_player.prepare
441 is_prepared = true
442 end
443
444 # Pauses playback
445 # REQUIRE `self.sound != null`
446 fun pause do
447 if self.error != null then return
448 assert sound != null
449 nmedia_player.pause
450 end
451
452 # Checks whether the mediaplayer is playing
453 fun playing: Bool do return nmedia_player.playing
454
455 # Releases the resources associated with this MediaPlayer
456 fun destroy do
457 nmedia_player.release
458 self.sound = null
459 end
460
461 # Reset MediaPlayer to its initial state
462 fun reset do nmedia_player.reset
463
464 # Sets the datasource (file-path or http/rtsp URL) to use
465 fun data_source(path: String): Music do
466 sys.jni_env.push_local_frame(1)
467 var retval = nmedia_player.data_source_path(path.to_java_string)
468 sys.jni_env.pop_local_frame
469 if retval == 0 then
470 self.error = new Error("could not load the sound " + path)
471 self.sound = new Music.priv_init(null, self, self.error)
472
473 else
474 self.sound = new Music.priv_init(null, self, null)
475 end
476 return self.sound.as(not null)
477 end
478 # Sets the data source (NativeFileDescriptor) to use
479 fun data_source_fd(fd: NativeAssetFileDescriptor): Music do
480 if not fd.is_java_null then
481 if nmedia_player.data_source_fd(fd.file_descriptor, fd.start_offset, fd.length) == 0 then
482 self.error = new Error("could not load the sound")
483 self.sound = new Music.priv_init(null, self, self.error)
484 else
485 self.sound = new Music.priv_init(null, self, null)
486 end
487 return self.sound.as(not null)
488 else
489 var error = new Error("could not load the sound")
490 return new Music.priv_init(null, self, error)
491 end
492 end
493
494 # Checks whether the MediaPlayer is looping or non-looping
495 fun looping: Bool do return nmedia_player.looping
496
497 # Sets the player to be looping or non-looping
498 fun looping=(b: Bool) do nmedia_player.looping = b
499
500 # Sets the volume on this player
501 fun volume=(volume: Float) do nmedia_player.volume = volume
502
503 # Sets the left volume and the right volume of this player
504 fun both_volume(left_volume, right_volume: Float) do nmedia_player.both_volume(left_volume, right_volume)
505
506 # Sets the audio stream type for this media player
507 fun stream_type=(stream_type: Int) do nmedia_player.stream_type = stream_type
508 end
509
510 redef class PlayableAudio
511 # Flag to know if the user paused the sound
512 # Used when the app pause all sounds or resume all sounds
513 var paused: Bool = false
514
515 # Is `self` already loaded?
516 protected var is_loaded = false is writable
517
518 redef var error = null
519 end
520
521 redef class Sound
522
523 # Resource ID of this sound
524 var id: nullable Int is noinit
525
526 # The SoundPool who loaded this sound
527 var soundpool: SoundPool is noinit
528
529 # The SoundID of this sound in his SoundPool
530 var soundpool_id: Int is noinit
531
532 private init priv_init(id: nullable Int, soundpool_id: Int, soundpool: SoundPool, error: nullable Error) is nosuper do
533 self.id = id
534 self.soundpool_id = soundpool_id
535 self.soundpool = soundpool
536 if error != null then
537 self.error = error
538 else
539 self.is_loaded = true
540 end
541 end
542
543 redef fun load do
544 if is_loaded then return
545
546 # Try resources (res)
547 var rid = app.default_soundpool.load_name_rid(app.resource_manager, app.native_activity, path.strip_extension)
548 if rid > 0 then
549 self.soundpool_id = rid
550 self.soundpool = app.default_soundpool
551 self.error = null
552 self.soundpool.error = null
553 else
554 # Try assets
555 var nam = app.asset_manager.open_fd(path)
556 if nam.is_java_null then
557 self.error = new Error("Failed to get file descriptor for " + path)
558 else
559 var retval_assets = app.default_soundpool.load_asset_fd_rid(nam)
560 nam.close
561 if retval_assets == -1 then
562 self.error = new Error("Failed to load " + path)
563 else
564 self.soundpool_id = retval_assets
565 self.soundpool = app.default_soundpool
566 self.error = null
567 self.soundpool.error = null
568 end
569 end
570 end
571 is_loaded = true
572
573 var error = error
574 if error != null then print_error error
575 end
576
577 redef fun play do
578 if not is_loaded then load
579 if self.error != null then return
580 soundpool.play(soundpool_id)
581 end
582
583 redef fun pause do
584 if self.error != null or not self.is_loaded then return
585 soundpool.pause_stream(soundpool_id)
586 paused = true
587 end
588
589 redef fun resume do
590 if self.error != null or not self.is_loaded then return
591 soundpool.resume(soundpool_id)
592 paused = false
593 end
594
595 end
596
597 redef class Music
598
599 # Resource ID of this sound
600 var id: nullable Int is noinit
601
602 # The MediaPlayer who loaded this sound
603 var media_player: MediaPlayer is noinit
604
605 private init priv_init(id: nullable Int, media_player: MediaPlayer, error: nullable Error) is nosuper do
606 self.id = id
607 self.media_player = media_player
608 if error != null then
609 self.error = error
610 else
611 self.is_loaded = true
612 end
613 end
614
615 redef fun load do
616 if is_loaded then return
617
618 # Try resources (res)
619 var rid = app.resource_manager.raw_id(path.strip_extension)
620 if rid > 0 then
621 var mp_sound_resources = app.default_mediaplayer.load_sound(rid, app.native_activity)
622 if mp_sound_resources.error != null then
623 self.media_player = app.default_mediaplayer
624 self.error = null
625 self.media_player.error = null
626 end
627 self.error = mp_sound_resources.error
628 else
629 # Try assets
630 var nam = app.asset_manager.open_fd(path)
631 if nam.is_java_null then
632 self.error = new Error("Failed to get file descriptor for " + path)
633 else
634 var mp_sound_assets = app.default_mediaplayer.data_source_fd(nam)
635 nam.close
636 if mp_sound_assets.error != null then
637 self.error = mp_sound_assets.error
638 else
639 self.media_player = app.default_mediaplayer
640 self.error = null
641 self.media_player.error = null
642 end
643 end
644 end
645 is_loaded = true
646
647 var error = error
648 if error != null then print_error error
649 end
650
651 redef fun play do
652 if not is_loaded then load
653 if self.error != null then return
654 media_player.start
655 end
656
657 redef fun pause do
658 if self.error != null or not self.is_loaded then return
659 media_player.pause
660 paused = true
661 end
662
663 redef fun resume do
664 if self.error != null or not self.is_loaded then return
665 play
666 paused = false
667 end
668 end
669
670 redef class App
671
672 # Returns the default MediaPlayer of the application.
673 # When you load a music, it goes in this MediaPlayer.
674 # Use it for advanced sound management
675 var default_mediaplayer: MediaPlayer is lazy do return new MediaPlayer
676
677 # Returns the default MediaPlayer of the application.
678 # When you load a short sound (not a music), it's added to this soundpool.
679 # Use it for advanced sound management.
680 var default_soundpool: SoundPool is lazy do return new SoundPool
681
682 # Get the native audio manager
683 private fun audio_manager(native_activity: NativeContext): NativeAudioManager in "Java" `{
684 return (AudioManager)native_activity.getSystemService(Context.AUDIO_SERVICE);
685 `}
686
687 # Sets the stream of the app to STREAM_MUSIC.
688 # STREAM_MUSIC is the default stream used by android apps.
689 private fun manage_audio_stream(native_activity: NativeActivity) in "Java" `{
690 native_activity.setVolumeControlStream(AudioManager.STREAM_MUSIC);
691 `}
692
693 # Same as `load_sound` but load the sound from the `res/raw` folder
694 fun load_sound_from_res(sound_name: String): Sound do
695 return default_soundpool.load_name(resource_manager, native_activity, sound_name)
696 end
697
698 # Same as `load_music` but load the sound from the `res/raw` folder
699 fun load_music_from_res(music: String): Music do
700 return default_mediaplayer.load_sound(resource_manager.raw_id(music), native_activity)
701 end
702
703 redef fun on_pause do
704 super
705 for s in sounds do
706 # Pausing sounds that are not already paused by user
707 # `s.paused` is set to false because `pause` set it to true
708 # and we want to know which sound has been paused by the user
709 # and which one has been paused by the app
710 if not s.paused then
711 s.pause
712 s.paused = false
713 end
714 end
715 audio_manager(native_activity).abandon_audio_focus
716 end
717
718 redef fun on_create do
719 super
720 audio_manager(native_activity).request_audio_focus
721 manage_audio_stream native_activity
722 end
723
724 redef fun on_resume do
725 super
726 audio_manager(native_activity).request_audio_focus
727 for s in sounds do
728 # Resumes only the sounds paused by the App
729 if not s.paused then s.resume
730 end
731 end
732 end