Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: Update flame_audio to AP 1.0.0 #1724

Merged
merged 11 commits into from
Jun 14, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 22 additions & 19 deletions doc/flame_audio/audio.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Audio

Playing audio is essential for any game, so we made it simple!
Playing audio is essential for most games, so we made it simple!

First you have to add [flame_audio](https://github.com/flame-engine/flame_audio) to your dependency
list in your `pubspec.yaml` file:
Expand All @@ -12,11 +12,10 @@ dependencies:

The latest version can be found on [pub.dev](https://pub.dev/packages/flame_audio/install).

After installing the `flame_audio` package you can add audio files in the assets section of your
After installing the `flame_audio` package, you can add audio files in the assets section of your
`pubspec.yaml` file. Make sure that the audio files exists in the paths that you provide.

The default directory for `FlameAudio` is `assets/audio` (which can be changed) and for `AudioPool`
the default directory is `assets/audio/sfx`.
The default directory for `FlameAudio` is `assets/audio` (which can be changed).
luanpotter marked this conversation as resolved.
Show resolved Hide resolved
luanpotter marked this conversation as resolved.
Show resolved Hide resolved

For the examples below, your `pubspec.yaml` file needs to contain something like this:

Expand All @@ -27,9 +26,6 @@ flutter:
- assets/audio/music.mp3
```

(The default directory for `FlameAudio` is `assets/audio` (which can be changed) and for `AudioPool`
the default directory is `assets/audio/sfx`.)

Then you have the following methods at your disposal:

```dart
Expand Down Expand Up @@ -63,13 +59,29 @@ You can use [the `Bgm` class](bgm.md) (via `FlameAudio.bgm`) to play looping bac
tracks. The `Bgm` class lets Flame automatically manage the pausing and resuming of background music
tracks when the game is backgrounded or comes back to the foreground.

You can use [the `AudioPool` class](audio_pool.md) if you want to fire quick sound effects on a very
luanpotter marked this conversation as resolved.
Show resolved Hide resolved
efficient manner. `AudioPool` will keep a pool of `AudioPlayer`s preloaded with a given sound, and
allow you to fire them very fast in quick succession.
luanpotter marked this conversation as resolved.
Show resolved Hide resolved

Some file formats that work across devices and that we recommend are: MP3, OGG and WAV.

This bridge library (flame_audio) uses [audioplayers](https://github.com/bluefireteam/audioplayers)
in order to allow for playing multiple sounds simultaneously (crucial in a game). You can check the
link for a more in-depth explanation.

Finally, you can pre-load your audios. Audios need to be stored in the memory the first time they
Both on `play` and `loop` you can pass an additional optional double parameter, the `volume`
(defaults to `1.0`).

Both the `play` and `loop` methods return an instance of an `AudioPlayer` from the
[audioplayers](https://github.com/bluefireteam/audioplayers) lib, that allows you to stop, pause and
configure other parameters.

In fact you can always use `AudioPlayer`s directly to gain full control over how your audio is played
-- the `FlameAudio` class is just a wrapper for common functionality.

## Caching

You can pre-load your audios. Audios need to be stored in the memory the first time they
luanpotter marked this conversation as resolved.
Show resolved Hide resolved
are requested; therefore, the first time you play each mp3 you might get a delay. In order to
pre-load your audios, just use:

Expand All @@ -81,7 +93,7 @@ You can load all your audios in the beginning in your game's `onLoad` method so
play smoothly. To load multiple audio files, use the `loadAll` method:

```dart
await FlameAudio.audioCache.loadAll(['explosion.mp3', 'music.mp3'])
await FlameAudio.audioCache.loadAll(['explosion.mp3', 'music.mp3']);
```

Finally, you can use the `clear` method to remove a file that has been loaded into the cache:
Expand All @@ -93,13 +105,4 @@ FlameAudio.audioCache.clear('explosion.mp3');
There is also a `clearCache` method, that clears the whole cache.

This might be useful if, for instance, your game has multiple levels and each has a different
set of sounds and music.

Both load methods return a `Future` for the `File`s loaded.

Both on `play` and `loop` you can pass an additional optional double parameter, the `volume`
(defaults to `1.0`).

Both the `play` and `loop` methods return an instance of an `AudioPlayer` from the
[audioplayers](https://github.com/bluefireteam/audioplayers) lib, that allows you to stop, pause and
configure other parameters.
set of sounds and music.
15 changes: 15 additions & 0 deletions doc/flame_audio/audio_pool.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# AudioPool

An AudioPool is a provider of AudioPlayers that leaves them pre-loaded with
local assets to minimize delays.
luanpotter marked this conversation as resolved.
Show resolved Hide resolved

A single Audio Pool always plays the same sound, probably a quick sound
luanpotter marked this conversation as resolved.
Show resolved Hide resolved
effect, like a laser shooting from your ship or a jump sound for your
platformer.

The advantage of using Audio Pool is that by configuring a minimum
(starting) size, and a maximum size, the pool will create and preload
some players, and allow them to be re-used many times.

You can use the helper method `FlameAudio.createPool` to create Audio
Pool instances using the same global `FlameAudio.audioCache`.
luanpotter marked this conversation as resolved.
Show resolved Hide resolved
40 changes: 9 additions & 31 deletions doc/flame_audio/bgm.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,12 @@
# Looping Background Music

With the `Bgm` class you can manage looping of background music tracks with regards to application
With the `Bgm` class, you can manage looping of background music tracks with regards to application
(or game) lifecycle state changes.

When the application is terminated, or sent to background, `Bgm` will automatically pause
the currently playing music track. Similarly, when the application is resumed, `Bgm` will resume the
background music. Manually pausing and resuming your tracks is also supported.

The `Bgm` class is handled by the [flame_audio](https://github.com/flame-engine/flame_audio) library
so you first have to add that to your dependency list in your `pubspec.yaml` file:

```yaml
dependencies:
flame_audio: <VERSION>
```

The latest version can be found on [pub.dev](https://pub.dev/packages/flame_audio/install).

For this class to function properly, the observer must be registered by calling the following:

```dart
Expand All @@ -42,32 +32,20 @@ import 'package:flame_audio/flame_audio.dart';
FlameAudio.bgm.play('adventure-track.mp3');
```

**Note:** The `Bgm` class will always use the static instance of `FlameAudio` for storing cached
music files.

You must have an appropriate folder structure and add the files to the `pubspec.yaml` file, as
explained in [Flame Audio documentation](audio.md).


## Caching music files

The following functions can be used to preload (and unload) music files into the cache. These
functions are just aliases for the ones in `FlameAudio` with the same names.
The `Bgm` class will use the static instance of `FlameAudio` for storing cached
music files by default.

Again, please refer to the [Flame Audio documentation](audio.md) if you need more info.

```dart
import 'package:flame_audio/flame_audio.dart';

FlameAudio.bgm.load('adventure-track.mp3');
FlameAudio.bgm.loadAll([
'menu.mp3',
'dungeon.mp3',
]);
FlameAudio.bgm.clear('adventure-track.mp3');
FlameAudio.bgm.clearCache();
```
So in order to pre-load music, you can use the same recommendations from the
[Flame Audio documentation](audio.md).

You can optionally create your own `Bgm` instances with different backing `AudioCache`s,
if you so desire.

## Methods

Expand All @@ -81,11 +59,11 @@ You can pass an additional optional `double` parameter which is the `volume` (de
Examples:

```dart
FlameAudio.bgm.play('bgm/boss-fight/level-382.mp3');
FlameAudio.bgm.play('music/boss-fight/level-382.mp3');
```

```dart
FlameAudio.bgm.play('bgm/world-map.mp3', volume: .25);
FlameAudio.bgm.play('music/world-map.mp3', volume: .25);
```


Expand Down
1 change: 1 addition & 0 deletions doc/flame_audio/flame_audio.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@

General audio <audio.md>
Background music <bgm.md>
AudioPool <audio_pool.md>
```
8 changes: 6 additions & 2 deletions packages/flame_audio/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ class AudioGame extends FlameGame with TapDetector {

@override
Future<void> onLoad() async {
pool = await AudioPool.create('fire_2.mp3', minPlayers: 3, maxPlayers: 4);
pool = await FlameAudio.createPool(
'sfx/fire_2.mp3',
luanpotter marked this conversation as resolved.
Show resolved Hide resolved
minPlayers: 3,
maxPlayers: 4,
);
startBgmMusic();
}

Expand All @@ -41,7 +45,7 @@ class AudioGame extends FlameGame with TapDetector {
}

void fireOne() {
FlameAudio.audioCache.play('sfx/fire_1.mp3');
FlameAudio.play('sfx/fire_1.mp3');
}

void fireTwo() {
Expand Down
48 changes: 17 additions & 31 deletions packages/flame_audio/lib/audio_pool.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,54 +6,47 @@ import 'package:synchronized/synchronized.dart';
/// Represents a function that can stop an audio playing.
typedef Stoppable = void Function();

/// An AudioPool is a provider of AudioPlayers that leaves them pre-loaded to
/// minimize delays.
/// An AudioPool is a provider of AudioPlayers that leaves them pre-loaded with
/// local assets to minimize delays.
luanpotter marked this conversation as resolved.
Show resolved Hide resolved
///
/// All AudioPlayers loaded are for the same [sound]. If you want multiple
/// sounds use multiple [AudioPool].
/// Use this class if you'd like have extremely quick firing, repetitive and
/// simultaneous sounds, like shooting a laser in a fast-paced spaceship game.
class AudioPool {
final AudioCache _cache;
final Map<String, AudioPlayer> _currentPlayers = {};
final List<AudioPlayer> _availablePlayers = [];

/// Instance of [AudioCache] to be used by all players.
final AudioCache audioCache;

/// The path of the sound of this pool.
final String sound;

/// If the pool is repeating.
final bool repeating;

/// Max and min numbers of players.
final int minPlayers, maxPlayers;

final Lock _lock = Lock();

AudioPool._(
this.sound, {
bool? repeating,
int? maxPlayers,
int? minPlayers = 1,
String? prefix,
}) : _cache = AudioCache(prefix: prefix ?? 'assets/audio/sfx/'),
repeating = repeating ?? false,
maxPlayers = maxPlayers ?? 1,
minPlayers = minPlayers ?? 1;
required this.minPlayers,
required this.maxPlayers,
AudioCache? audioCache,
}) : audioCache = audioCache ?? AudioCache.instance;

/// Creates an [AudioPool] instance with the given parameters.
static Future<AudioPool> create(
String sound, {
bool? repeating,
int? maxPlayers,
int? minPlayers = 1,
String? prefix,
AudioCache? audioCache,
int minPlayers = 1,
required int maxPlayers,
}) async {
final instance = AudioPool._(
sound,
repeating: repeating,
audioCache: audioCache,
maxPlayers: maxPlayers,
minPlayers: minPlayers,
prefix: prefix,
);
for (var i = 0; i < instance.minPlayers; i++) {
instance._availablePlayers.add(await instance._createNewAudioPlayer());
Expand Down Expand Up @@ -90,23 +83,16 @@ class AudioPool {
});
}

subscription = player.onPlayerCompletion.listen((_) {
if (repeating) {
player.resume();
} else {
stop();
}
});
subscription = player.onPlayerComplete.listen((_) => stop());

return stop;
});
}

Future<AudioPlayer> _createNewAudioPlayer() async {
final player = AudioPlayer();
final url = (await _cache.load(sound)).path;
await player.setUrl(url);
await player.setReleaseMode(ReleaseMode.STOP);
final player = AudioPlayer()..audioCache = audioCache;
await player.setSource(AssetSource(sound));
await player.setReleaseMode(ReleaseMode.stop);
return player;
}
}
Loading