Merge pull request #598 from vlidholt/master
Adds rough sound support for sprites
diff --git a/examples/game/lib/game_demo_world.dart b/examples/game/lib/game_demo_world.dart
index f86816d..cc683e6 100644
--- a/examples/game/lib/game_demo_world.dart
+++ b/examples/game/lib/game_demo_world.dart
@@ -28,6 +28,10 @@
SpriteSheet _spriteSheet;
SpriteSheet _spriteSheetUI;
+
+ Map<String,SoundEffect> _sounds;
+ SoundPool _soundPool = SoundPool.sharedInstance();
+
Navigator _navigator;
// Inputs
@@ -54,7 +58,7 @@
Function _gameOverCallback;
- GameDemoWorld(App app, this._navigator, ImageMap images, this._spriteSheet, this._spriteSheetUI, this._gameOverCallback) : super(new Size(_gameSizeWidth, _gameSizeHeight)) {
+ GameDemoWorld(App app, this._navigator, ImageMap images, this._spriteSheet, this._spriteSheetUI, this._sounds, this._gameOverCallback) : super(new Size(_gameSizeWidth, _gameSizeHeight)) {
// Fetch images
_imgNebula = images["assets/nebula.png"];
@@ -140,6 +144,8 @@
laser.constrainProportions = true;
_lasers.add(laser);
_gameLayer.addChild(laser);
+
+ _soundPool.play(_sounds["laser"]);
}
void addNebula() {
@@ -225,6 +231,8 @@
}
_gameLayer.addChild(explosionNode);
+
+ _soundPool.play(_sounds["explosion"]);
}
void update(double dt) {
diff --git a/examples/game/lib/main.dart b/examples/game/lib/main.dart
index b142cde..68ca68d 100644
--- a/examples/game/lib/main.dart
+++ b/examples/game/lib/main.dart
@@ -28,6 +28,7 @@
SpriteSheet _spriteSheet;
SpriteSheet _spriteSheetUI;
GameDemoApp _app;
+Map<String,SoundEffect> _sounds = {};
main() async {
_loader = new ImageMap(_bundle);
@@ -47,6 +48,12 @@
_app = new GameDemoApp();
+ _sounds["explosion"] = new SoundEffect('https://github.com/slembcke/GalacticGuardian.spritebuilder/raw/GDC/Packages/SpriteBuilder%20Resources.sbpack/TempSounds/Explosion.wav');
+ _sounds["laser"] = new SoundEffect('https://github.com/slembcke/GalacticGuardian.spritebuilder/raw/GDC/Packages/SpriteBuilder%20Resources.sbpack/TempSounds/Laser.wav');
+
+ await _sounds["explosion"].load();
+ await _sounds["laser"].load();
+
runApp(_app);
}
@@ -103,6 +110,7 @@
_loader,
_spriteSheet,
_spriteSheetUI,
+ _sounds,
(lastScore) {
setState(() {_lastScore = lastScore;});
}
diff --git a/examples/game/lib/sound.dart b/examples/game/lib/sound.dart
new file mode 100644
index 0000000..51c9740
--- /dev/null
+++ b/examples/game/lib/sound.dart
@@ -0,0 +1,162 @@
+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 SoundCompleteCallback();
+
+class SoundEffect {
+ SoundEffect(this._url);
+
+ // TODO: Remove load method from SoundEffect
+ Future load() async {
+ UrlResponse response = await fetchUrl(_url);
+ _data = response.body;
+ }
+
+ String _url;
+ Object _data;
+}
+
+class SoundStream {
+ SoundStream(
+ this.sound,
+ this.tag,
+ this.loop,
+ this.time,
+ this.volume,
+ this.pitch,
+ this.pan,
+ this.callback
+ );
+
+ // TODO: Make these properties work
+ SoundEffect sound;
+ bool playing = false;
+ bool loop = false;
+ double time = 0.0;
+ double volume = 1.0;
+ double pitch = 1.0;
+ double pan = 0.0;
+ Object tag;
+
+ // TODO: Implement completion callback. On completion, sounds should
+ // also be removed from the list of playing sounds.
+ SoundCompleteCallback callback;
+
+ MediaPlayerProxy _player;
+}
+
+SoundPool _sharedSoundPool;
+
+class SoundPool {
+
+ static SoundPool sharedInstance() {
+ if (_sharedSoundPool == null) {
+ _sharedSoundPool = new SoundPool();
+ }
+ return _sharedSoundPool;
+ }
+
+ SoundPool() {
+ _mediaService = new MediaServiceProxy.unbound();
+ shell.requestService(null, _mediaService);
+ }
+
+ MediaServiceProxy _mediaService;
+ List<SoundStream> _playingSounds = [];
+
+ // TODO: This should no longer be needed when moving to SoundPool backing
+ Map<SoundEffect,MediaPlayerProxy> _mediaPlayers = {};
+
+ Future _prepare(SoundStream 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;
+
+ SoundStream play(
+ SoundEffect sound,
+ [Object tag,
+ bool loop = false,
+ double volume = 1.0,
+ double pitch = 1.0,
+ double pan = 0.0,
+ double startTime = 0.0,
+ SoundCompleteCallback callback = null]) {
+
+ // Create new PlayingSound object
+ SoundStream playingSound = new SoundStream(
+ sound,
+ tag,
+ loop,
+ startTime,
+ 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((startTime * 1000.0).toInt());
+ playingSound._player.ptr.start();
+ });
+
+ _playingSounds.add(playingSound);
+ _mediaPlayers[sound] = playingSound._player;
+ } else {
+ // Reuse player
+ playingSound._player = _mediaPlayers[sound];
+ playingSound._player.ptr.seekTo((startTime * 1000.0).toInt());
+ playingSound._player.ptr.start();
+ }
+
+ return playingSound;
+ }
+
+ void stop(Object tag) {
+ for (int i = _playingSounds.length; i >= 0; i--) {
+ SoundStream playingSound = _playingSounds[i];
+ if (playingSound.tag == tag) {
+ playingSound._player.ptr.pause();
+ _playingSounds.removeAt(i);
+ }
+ }
+ }
+
+ List<SoundStream> playingSoundsForTag(Object tag) {
+ List<SoundStream> list = [];
+ for (SoundStream playingSound in _playingSounds) {
+ if (playingSound.tag == tag) {
+ list.add(playingSound);
+ }
+ }
+ return list;
+ }
+
+ void stopAll() {
+ for (SoundStream playingSound in _playingSounds) {
+ playingSound._player.ptr.pause();
+ }
+ _playingSounds = [];
+ }
+}
diff --git a/examples/game/lib/sprites.dart b/examples/game/lib/sprites.dart
index 801dbc0..155dcc2 100644
--- a/examples/game/lib/sprites.dart
+++ b/examples/game/lib/sprites.dart
@@ -10,12 +10,16 @@
import 'dart:typed_data';
import 'dart:sky';
+import 'package:mojo/mojo/url_response.mojom.dart';
import 'package:sky/animation/curves.dart';
import 'package:sky/base/scheduler.dart' as scheduler;
import 'package:sky/mojo/asset_bundle.dart';
+import 'package:sky/mojo/net/fetch.dart';
+import 'package:sky/mojo/shell.dart' as shell;
import 'package:sky/rendering/box.dart';
import 'package:sky/rendering/object.dart';
import 'package:sky/widgets/framework.dart';
+import 'package:sky_services/media/media.mojom.dart';
import 'package:vector_math/vector_math.dart';
part 'action.dart';
@@ -26,6 +30,7 @@
part 'node3d.dart';
part 'node_with_size.dart';
part 'particle_system.dart';
+part 'sound.dart';
part 'sprite.dart';
part 'spritesheet.dart';
part 'sprite_box.dart';