| part of sprites; |
| |
| // TODO: The sound effects should probably use Android's SoundPool instead of |
| // MediaPlayer as it is more efficient and flexible for playing back sound effects |
| |
| typedef void SoundEffectStreamCallback(SoundEffectStream); |
| |
| class SoundEffect { |
| SoundEffect(this._pipeFuture); |
| |
| // TODO: Remove load method from SoundEffect |
| Future load() async { |
| _data = await _pipeFuture; |
| } |
| |
| Future<MojoDataPipeConsumer> _pipeFuture; |
| MojoDataPipeConsumer _data; |
| } |
| |
| class SoundEffectStream { |
| SoundEffectStream( |
| this.sound, |
| this.loop, |
| this.volume, |
| this.pitch, |
| this.pan, |
| this.onSoundComplete |
| ); |
| |
| // TODO: Make these properties work |
| SoundEffect sound; |
| bool playing = false; |
| bool loop = false; |
| double volume = 1.0; |
| double pitch = 1.0; |
| double pan = 0.0; |
| |
| // TODO: Implement completion callback. On completion, sounds should |
| // also be removed from the list of playing sounds. |
| SoundEffectStreamCallback onSoundComplete; |
| |
| MediaPlayerProxy _player; |
| } |
| |
| SoundEffectPlayer _sharedSoundEffectPlayer; |
| |
| class SoundEffectPlayer { |
| |
| static SoundEffectPlayer sharedInstance() { |
| if (_sharedSoundEffectPlayer == null) { |
| _sharedSoundEffectPlayer = new SoundEffectPlayer(); |
| } |
| return _sharedSoundEffectPlayer; |
| } |
| |
| SoundEffectPlayer() { |
| _mediaService = new MediaServiceProxy.unbound(); |
| shell.requestService(null, _mediaService); |
| } |
| |
| MediaServiceProxy _mediaService; |
| List<SoundEffectStream> _soundEffectStreams = []; |
| |
| // TODO: This should no longer be needed when moving to SoundPool backing |
| Map<SoundEffect,MediaPlayerProxy> _mediaPlayers = {}; |
| |
| Future _prepare(SoundEffectStream playingSound) async { |
| await playingSound._player.ptr.prepare(playingSound.sound._data); |
| } |
| |
| // TODO: Move sound loading here |
| // TODO: Support loading sounds from bundles |
| // Future<SoundEffect> load(url) async { |
| // ... |
| // } |
| |
| // TODO: Add sound unloader |
| // unload(SoundEffect effect) { |
| // ... |
| // } |
| |
| // TODO: Add paused property (should pause playback of all sounds) |
| bool paused; |
| |
| SoundEffectStream play( |
| SoundEffect sound, |
| [bool loop = false, |
| double volume = 1.0, |
| double pitch = 1.0, |
| double pan = 0.0, |
| SoundEffectStreamCallback callback = null]) { |
| |
| // Create new PlayingSound object |
| SoundEffectStream playingSound = new SoundEffectStream( |
| sound, |
| loop, |
| volume, |
| pitch, |
| pan, |
| callback |
| ); |
| |
| // TODO: Replace this with calls to SoundPool |
| if (_mediaPlayers[sound] == null) { |
| // Create player |
| playingSound._player = new MediaPlayerProxy.unbound(); |
| _mediaService.ptr.createPlayer(playingSound._player); |
| |
| // Prepare sound, then play it |
| _prepare(playingSound).then((_) { |
| playingSound._player.ptr.seekTo(0); |
| playingSound._player.ptr.start(); |
| }); |
| |
| _soundEffectStreams.add(playingSound); |
| _mediaPlayers[sound] = playingSound._player; |
| } else { |
| // Reuse player |
| playingSound._player = _mediaPlayers[sound]; |
| playingSound._player.ptr.seekTo(0); |
| playingSound._player.ptr.start(); |
| } |
| |
| return playingSound; |
| } |
| |
| void stop(SoundEffectStream stream) { |
| stream._player.ptr.pause(); |
| _soundEffectStreams.remove(stream); |
| } |
| |
| void stopAll() { |
| for (SoundEffectStream playingSound in _soundEffectStreams) { |
| playingSound._player.ptr.pause(); |
| } |
| _soundEffectStreams = []; |
| } |
| } |
| |
| typedef void SoundTrackCallback(SoundTrack); |
| typedef void SoundTrackBufferingCallback(SoundTrack, int); |
| |
| class SoundTrack { |
| MediaPlayerProxy _player; |
| |
| SoundTrackCallback onSoundComplete; |
| SoundTrackCallback onSeekComplete; |
| SoundTrackBufferingCallback onBufferingUpdate; |
| bool loop; |
| double time; |
| double volume; |
| } |
| |
| SoundTrackPlayer _sharedSoundTrackPlayer; |
| |
| class SoundTrackPlayer { |
| List<SoundTrack> _soundTracks = []; |
| |
| static sharedInstance() { |
| if (_sharedSoundTrackPlayer == null) { |
| _sharedSoundTrackPlayer = new SoundTrackPlayer(); |
| } |
| return _sharedSoundTrackPlayer; |
| } |
| |
| SoundTrackPlayer() { |
| _mediaService = new MediaServiceProxy.unbound(); |
| shell.requestService(null, _mediaService); |
| } |
| |
| MediaServiceProxy _mediaService; |
| |
| Future<SoundTrack> load(Future<MojoDataPipeConsumer> pipe) async { |
| // Create media player |
| SoundTrack soundTrack = new SoundTrack(); |
| soundTrack._player = new MediaPlayerProxy.unbound(); |
| _mediaService.ptr.createPlayer(soundTrack._player); |
| |
| await soundTrack._player.ptr.prepare(await pipe); |
| return soundTrack; |
| } |
| |
| void unload(SoundTrack soundTrack) { |
| stop(soundTrack); |
| _soundTracks.remove(soundTrack); |
| } |
| |
| void play( |
| SoundTrack soundTrack, |
| [bool loop = false, |
| double volume, |
| double startTime = 0.0]) { |
| // TODO: Implement looping & volume |
| // soundTrack._player.ptr.setLooping(loop); |
| // soundTrack._player.ptr.setVolume(volume); |
| soundTrack._player.ptr.seekTo((startTime * 1000.0).toInt()); |
| soundTrack._player.ptr.start(); |
| } |
| |
| void stop(SoundTrack track) { |
| track._player.ptr.pause(); |
| } |
| |
| void stopAll() { |
| for (SoundTrack soundTrack in _soundTracks) { |
| soundTrack._player.ptr.pause(); |
| } |
| } |
| } |