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