sound.dart 7.16 KB
Newer Older
1 2 3 4
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
part of flutter_sprites;
6

7
/// An audio asset loaded by the SoundEffectPlayer.
8
class SoundEffect {
9 10 11

  /// Creates a new sound effect with the given sound id. Normally,
  /// SoundEffect objects are created through the [SoundEffectPlayer].
12
  SoundEffect(this._soundId);
13

14
  int _soundId;
15 16
}

17
/// A sound being played by the SoundEffectPlayer.
18
class SoundEffectStream {
19 20 21

  /// Creates a new SoundEffectStream. Typically SoundEffectStream objects are
  /// created by the SoundEffectPlayer.
22 23 24 25 26 27 28 29 30 31 32 33
  SoundEffectStream(SoundEffectPlayer player, int streamId, {
    double leftVolume,
    double rightVolume,
    double pitch
  }) {
    _player = player;
    _streamId = streamId;
    _paused = false;
    _leftVolume = leftVolume;
    _rightVolume = rightVolume;
    _pitch = pitch;
  }
34

35 36
  SoundEffectPlayer _player;
  int _streamId;
37

38
  SoundPoolProxy get _soundPool => _player._soundPool;
39

40
  /// Stop the sound effect.
41
  void stop() {
42
    _soundPool.stop(_streamId);
43
  }
44

45
  /// True if the sound effect is paused.
46 47
  bool get paused => _paused;
  bool _paused;
48
  set paused(bool value) {
49 50
    _paused = value;
    if (_paused) {
51
      _soundPool.pause(_streamId);
52
    } else {
53
      _soundPool.resume(_streamId);
54 55 56
    }
  }

57
  /// Left volume of the sound effect, valid values are 0.0 to 1.0.
58 59
  double get leftVolume => _leftVolume;
  double _leftVolume;
60
  set leftVolume(double value) {
61
    _leftVolume = value;
62
    _soundPool.setVolume(_streamId, <double>[_leftVolume, _rightVolume]);
63 64
  }

65
  /// Right volume of the sound effect, valid values are 0.0 to 1.0.
66 67
  double get rightVolume => _rightVolume;
  double _rightVolume;
68
  set rightVolume(double value) {
69
    _rightVolume = value;
70
    _soundPool.setVolume(_streamId, <double>[_leftVolume, _rightVolume]);
71
  }
72

73 74
  /// The pitch of the sound effect, a value of 1.0 plays back the sound effect
  /// at normal speed. Cannot be negative.
75 76
  double get pitch => _pitch;
  double _pitch;
77
  set pitch(double value) {
78
    _pitch = value;
79
    _soundPool.setRate(_streamId, _pitch);
80 81
  }
}
82

83
/// The SoundEffectPlayer loads and plays sound effects.
84
class SoundEffectPlayer {
85 86 87

  /// Creates a new SoundEffectPlayer with a max number of simultaneous
  /// streams specified.
88
  SoundEffectPlayer(int maxStreams) {
89 90 91
    MediaServiceProxy mediaService = shell.connectToApplicationService(
      'mojo:media_service', MediaService.connectToService
    );
92
    _soundPool = new SoundPoolProxy.unbound();
93
    mediaService.createSoundPool(_soundPool, maxStreams);
94 95
  }

96 97 98
  SoundPoolProxy _soundPool;
  bool _paused;
  int _nextStreamId = 0;
99

100
  /// Loads a sound effect.
101 102 103 104 105 106 107 108 109
  Future<SoundEffect> load(MojoDataPipeConsumer data) {
    Completer<SoundEffect> completer = new Completer<SoundEffect>();
    _soundPool.load(data, (bool success, int soundId) {
      if (success)
        completer.complete(new SoundEffect(soundId));
      else
        completer.completeError(new Exception('Unable to load sound'));
    });
    return completer.future;
110
  }
111

112
  /// Plays a sound effect.
113 114 115 116 117
  Future<SoundEffectStream> play(SoundEffect sound, {
    double leftVolume: 1.0,
    double rightVolume: 1.0,
    bool loop: false,
    double pitch: 1.0
118 119
  }) {
    Completer<SoundEffectStream> completer = new Completer<SoundEffectStream>();
120
    int streamId = _nextStreamId++;
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
    _soundPool.play(sound._soundId,
                    streamId,
                    <double>[leftVolume, rightVolume],
                    loop,
                    pitch,
                    (bool success) {
      if (success) {
        completer.complete(new SoundEffectStream(this, streamId,
          leftVolume: leftVolume,
          rightVolume: rightVolume,
          pitch: pitch
        ));
      } else {
        completer.completeError(new Exception('Unable to play sound'));
      }
    });
    return completer.future;
138 139
  }

140
  /// Set to true to pause a sound effect.
141
  bool get paused => _paused;
142

143
  set paused(bool value) {
144 145
    _paused = value;
    if (_paused) {
146
      _soundPool.pauseAll();
147
    } else {
148
      _soundPool.resumeAll();
149 150 151
    }
  }
}
152

153
/// Signature for callbacks used by [SoundTrack].
Hixie's avatar
Hixie committed
154
typedef void SoundTrackCallback(SoundTrack soundTrack);
155 156

/// Signature for callbacks used by [SoundTrack].
Hixie's avatar
Hixie committed
157
typedef void SoundTrackBufferingCallback(SoundTrack soundTrack, int index);
158

159 160
/// A sound track is typically longer than a [SoundEffect]. Use sound tracks to
/// play back music or ambient sounds.
161 162 163
class SoundTrack {
  MediaPlayerProxy _player;

164
  /// Called when the sound has finished playing.
165
  SoundTrackCallback onSoundComplete;
166 167

  /// Called when a seek operation has finished.
168
  SoundTrackCallback onSeekComplete;
169 170

  /// Called when buffering is being performed.
171
  SoundTrackBufferingCallback onBufferingUpdate;
172 173

  /// If true, the sound track will automatically loop.
174
  bool loop;
175 176

  /// The current playback time in seconds.
177
  double time;
178 179 180

  /// The volume the sound track is currently played at, valid range is 0.0 to
  /// 1.0.
181
  double volume;
182 183 184 185
}

SoundTrackPlayer _sharedSoundTrackPlayer;

186
/// Loads and plays [SoundTrack]s.
187
class SoundTrackPlayer {
188 189 190

  /// Creates a new [SoundTrackPlayer], typically you will want to use the
  /// [sharedInstance] method to receive the player.
191
  SoundTrackPlayer() {
192 193 194
    _mediaService = shell.connectToApplicationService(
      'mojo:media_service', MediaService.connectToService
    );
195 196 197 198
  }

  MediaServiceProxy _mediaService;

Ian Hickson's avatar
Ian Hickson committed
199 200
  Set<SoundTrack> _soundTracks = new HashSet<SoundTrack>();

201 202
  /// Retrives a singleton object of the SoundTrackPlayer, use this method
  /// in favor for the constructor.
Ian Hickson's avatar
Ian Hickson committed
203 204 205 206
  static SoundTrackPlayer sharedInstance() {
    return _sharedSoundTrackPlayer ??= new SoundTrackPlayer();
  }

207
  /// Loads a [SoundTrack].
208
  Future<SoundTrack> load(Future<MojoDataPipeConsumer> pipe) async {
209 210 211
    // Create media player
    SoundTrack soundTrack = new SoundTrack();
    soundTrack._player = new MediaPlayerProxy.unbound();
212
    _mediaService.createPlayer(soundTrack._player);
213

214 215 216 217 218
    Completer<SoundTrack> completer = new Completer<SoundTrack>();
    soundTrack._player.prepare(await pipe, (bool ignored) {
      completer.complete(soundTrack);
    });
    return await completer.future;
219 220
  }

221
  /// Unloads a [SoundTrack] from memory.
222 223 224 225 226
  void unload(SoundTrack soundTrack) {
    stop(soundTrack);
    _soundTracks.remove(soundTrack);
  }

227
  /// Plays a [SoundTrack].
228 229 230 231 232
  void play(SoundTrack soundTrack, {
    bool loop: false,
    double volume: 1.0,
    double startTime: 0.0
  }) {
233 234 235 236 237
    soundTrack._player
      ..setLooping(loop)
      ..setVolume(volume)
      ..seekTo((startTime * 1000.0).toInt())
      ..start();
238
    _soundTracks.add(soundTrack);
239 240
  }

241 242
  /// Stops a [SoundTrack]. You may also want to call the [unload] method to
  /// remove if from memory if you are not planning to play it again.
243
  void stop(SoundTrack track) {
244
    track._player.pause();
245 246
  }

247
  /// Pauses all [SoundTrack]s that are currently playing.
248 249
  void pauseAll() {
    for (SoundTrack soundTrack in _soundTracks)
250
      soundTrack._player.pause();
251 252
  }

253
  /// Resumes all [SoundTrack]s that have been loaded by this player.
254 255
  void resumeAll() {
    for (SoundTrack soundTrack in _soundTracks)
256
      soundTrack._player.start();
257 258
  }
}