Move flame_audio & update to work with current rc11 (#807)

This commit is contained in:
Luan Nico
2021-05-21 22:37:30 -04:00
committed by GitHub
parent 5333164a9e
commit 77a2173869
19 changed files with 744 additions and 0 deletions

View File

@ -15,4 +15,5 @@ Bridge packages are packages which:
## Index
- [Flame](./flame)
- [Flame Audio](./flame_audio)
- [Fire Atlas](./flame_fire_atlas)

75
packages/flame_audio/.gitignore vendored Normal file
View File

@ -0,0 +1,75 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
build/
# Android related
**/android/**/gradle-wrapper.jar
**/android/.gradle
**/android/captures/
**/android/gradlew
**/android/gradlew.bat
**/android/local.properties
**/android/**/GeneratedPluginRegistrant.java
# iOS/XCode related
**/ios/**/*.mode1v3
**/ios/**/*.mode2v3
**/ios/**/*.moved-aside
**/ios/**/*.pbxuser
**/ios/**/*.perspectivev3
**/ios/**/*sync/
**/ios/**/.sconsign.dblite
**/ios/**/.tags*
**/ios/**/.vagrant/
**/ios/**/DerivedData/
**/ios/**/Icon?
**/ios/**/Pods/
**/ios/**/.symlinks/
**/ios/**/profile
**/ios/**/xcuserdata
**/ios/.generated/
**/ios/Flutter/App.framework
**/ios/Flutter/Flutter.framework
**/ios/Flutter/Flutter.podspec
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/app.flx
**/ios/Flutter/app.zip
**/ios/Flutter/flutter_assets/
**/ios/Flutter/flutter_export_environment.sh
**/ios/ServiceDefinitions.json
**/ios/Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!**/ios/**/default.mode1v3
!**/ios/**/default.mode2v3
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages

View File

@ -0,0 +1,24 @@
## [next]
* Moved project to the mono-repo
* Updated flame, audioplayers
## [0.1.0-rc5]
* Updated `audioplayers` version
* Updated Flame version
* Add support to null safety
## [0.1.0-rc4]
* Update audioplayers version
## [0.1.0-rc3]
* Bump flame version to rc5, upgrade code to match, update other dependencies
* Add linter and fix code to comply
## [0.1.0-rc2]
* Bump flame version to rc2 and audioplayers version to 0.17
## [0.1.0-rc1]
* First real version, including all necessary files for 1.0.0
## [0.0.1]
* Empty release; in the future all flame audio related code will live here.

View File

@ -0,0 +1 @@
../../LICENSE

View File

@ -0,0 +1,28 @@
# flame_audio
<p align="center">
<a href="https://flame-engine.org">
<img alt="flame" width="200px" src="https://user-images.githubusercontent.com/6718144/101553774-3bc7b000-39ad-11eb-8a6a-de2daa31bd64.png">
</a>
</p>
<p align="center">
Adds audio support for <a href="https://github.com/flame-engine/flame">Flame</a> using the <a href="https://github.com/luanpotter/audioplayers">audioplayers</a> package.
</p>
<p align="center">
<a title="Pub" href="https://pub.dartlang.org/packages/flame_audio" ><img src="https://img.shields.io/pub/v/flame_audio.svg?style=popout&include_prereleases" /></a>
<img src="https://github.com/flame-engine/flame_audio/workflows/Lint/badge.svg?branch=master&event=push" alt="Test" />
<a title="Discord" href="https://discord.gg/pxrBmy4" ><img src="https://img.shields.io/discord/509714518008528896.svg" /></a>
</p>
---
This package makes it easy to add audio capabilities to your games, integrating [Audioplayers](https://github.com/luanpotter/audioplayers) features seamless into your Flame game code.
Add this as a dependency alongside Flame v1 to your game if you want to play background music, ambient sounds, sound effects, etc.
# Credits
* The [Flame Engine team](https://github.com/orgs/flame-engine/people), who is continuously working on maintaining and improving Flame.
* [luanpotter's audioplayers](https://github.com/luanpotter/audioplayer) lib, which was originally a fork from [rxlabz's audioplayer](https://github.com/rxlabz/audioplayer).

View File

@ -0,0 +1,149 @@
# Source of linter options:
# http://dart-lang.github.io/linter/lints/options/options.html
analyzer:
strong-mode:
implicit-casts: false
implicit-dynamic: false
plugins:
- dart_code_metrics
linter:
rules:
- always_declare_return_types
- always_put_control_body_on_new_line
- always_require_non_null_named_parameters
- annotate_overrides
- avoid_double_and_int_checks
- avoid_dynamic_calls
- avoid_empty_else
- avoid_equals_and_hash_code_on_mutable_classes
- avoid_escaping_inner_quotes
- avoid_field_initializers_in_const_classes
- avoid_init_to_null
- avoid_js_rounded_ints
- avoid_null_checks_in_equality_operators
- avoid_private_typedef_functions
- avoid_redundant_argument_values
- avoid_relative_lib_imports
- avoid_return_types_on_setters
- avoid_shadowing_type_parameters
- avoid_slow_async_io
- avoid_type_to_string
- avoid_types_as_parameter_names
- avoid_unused_constructor_parameters
- await_only_futures
- camel_case_extensions
- camel_case_types
- cancel_subscriptions
- cast_nullable_to_non_nullable
- close_sinks
- comment_references
- constant_identifier_names
- control_flow_in_finally
- curly_braces_in_flow_control_structures
- directives_ordering
- do_not_use_environment
- empty_catches
- empty_constructor_bodies
- empty_statements
- exhaustive_cases
- file_names
- hash_and_equals
- implementation_imports
- invariant_booleans
- iterable_contains_unrelated_type
- join_return_with_assignment
- library_names
- library_prefixes
- list_remove_unrelated_type
- literal_only_boolean_expressions
- missing_whitespace_between_adjacent_strings
- no_adjacent_strings_in_list
- no_duplicate_case_values
- no_runtimeType_toString
- omit_local_variable_types
- package_api_docs
- package_names
- package_prefixed_library_names
- parameter_assignments
- prefer_adjacent_string_concatenation
- prefer_asserts_in_initializer_lists
- prefer_collection_literals
- prefer_conditional_assignment
- prefer_const_constructors
- prefer_const_constructors_in_immutables
- prefer_const_declarations
- prefer_const_literals_to_create_immutables
- prefer_contains
- prefer_equal_for_default_values
- prefer_final_fields
- prefer_final_in_for_each
- prefer_final_locals
- prefer_for_elements_to_map_fromIterable
- prefer_foreach
- prefer_function_declarations_over_variables
- prefer_generic_function_type_aliases
- prefer_if_elements_to_conditional_expressions
- prefer_if_null_operators
- prefer_initializing_formals
- prefer_inlined_adds
- prefer_interpolation_to_compose_strings
- prefer_is_empty
- prefer_is_not_empty
- prefer_is_not_operator
- prefer_iterable_whereType
- prefer_mixin
- prefer_null_aware_operators
- prefer_single_quotes
- prefer_spread_collections
- prefer_relative_imports
- prefer_typing_uninitialized_variables
- prefer_void_to_null
- provide_deprecation_message
- recursive_getters
- slash_for_doc_comments
- sort_unnamed_constructors_first
- test_types_in_equals
- throw_in_finally
- type_annotate_public_apis
- type_init_formals
- unnecessary_await_in_return
- unnecessary_brace_in_string_interps
- unnecessary_const
- unnecessary_getters_setters
- unnecessary_lambdas
- unnecessary_new
- unnecessary_null_aware_assignments
- unnecessary_null_in_if_null_operators
- unnecessary_overrides
- unnecessary_parenthesis
- unnecessary_raw_strings
- unnecessary_statements
- unnecessary_string_escapes
- unnecessary_string_interpolations
- unnecessary_this
- use_full_hex_values_for_flutter_colors
- use_function_type_syntax_for_parameters
- use_is_even_rather_than_modulo
- use_rethrow_when_possible
- unrelated_type_equality_checks
- unsafe_html
- void_checks
dart_code_metrics:
rules:
- prefer-trailing-comma
- prefer-trailing-comma-for-collection
- no-equal-then-else
- no-object-declaration
- potential-null-dereference
metrics-exclude:
- test/**
metrics:
number-of-parameters: 8
number-of-methods: 32
source-lines-of-code: 200
cyclomatic-complexity: 36
maximum-nesting-level: 8

51
packages/flame_audio/example/.gitignore vendored Normal file
View File

@ -0,0 +1,51 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Exceptions to above rules.
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
android
ios
web
integration_test
macos
test

View File

@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: a2bde82fbd52e09057a4146f46889f4e10342d32
channel: beta
project_type: app

View File

@ -0,0 +1,6 @@
# Credits
CC-0 audio files for this examples obtained here:
* sfx: https://opengameart.org/content/63-digital-sound-effects-lasers-phasers-space-etc
* bg music: https://opengameart.org/content/another-space-background-track

View File

@ -0,0 +1,3 @@
# flame audio example
Simple project to showcase the usage of flame_audio

View File

@ -0,0 +1,81 @@
import 'package:flame/components.dart';
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
import 'package:flame/gestures.dart';
import 'package:flame/palette.dart';
import 'package:flame_audio/audio_pool.dart';
import 'package:flame_audio/flame_audio.dart';
import 'package:flutter/widgets.dart' hide Animation;
void main() async {
runApp(GameWidget(game: AudioGame()));
}
/// This example game showcases three possible use cases:
///
/// 1. Use the static FlameAudio class to easily fire a sfx using the default
/// configs for the button tap.
/// 2. Uses a custom AudioPool for extremely efficient audio loading and pooling
/// for tapping elsewhere.
/// 3. Uses the Bgm utility for background music.
class AudioGame extends BaseGame with TapDetector {
static Paint black = BasicPalette.black.paint();
static Paint gray = const PaletteEntry(Color(0xFFCCCCCC)).paint();
static TextPaint text = TextPaint(
config: TextPaintConfig(color: BasicPalette.white.color),
);
late AudioPool pool;
@override
Future<void> onLoad() async {
pool = await AudioPool.create('fire_2.mp3', minPlayers: 3, maxPlayers: 4);
startBgmMusic();
}
Rect get button => Rect.fromLTWH(20, size.y - 300, size.x - 40, 200);
void startBgmMusic() {
FlameAudio.bgm.initialize();
FlameAudio.bgm.play('music/bg_music.ogg');
}
void fireOne() {
FlameAudio.audioCache.play('sfx/fire_1.mp3');
}
void fireTwo() {
pool.start();
}
@override
void render(Canvas canvas) {
super.render(canvas);
canvas.drawRect(size.toRect(), black);
text.render(
canvas,
'(click anywhere for 1)',
Vector2(size.x / 2, 200),
anchor: Anchor.topCenter,
);
canvas.drawRect(button, gray);
text.render(
canvas,
'click here for 2',
Vector2(size.x / 2, size.y - 200),
anchor: Anchor.bottomCenter,
);
}
@override
void onTapDown(TapDownInfo details) {
if (button.containsPoint(details.eventPosition.game)) {
fireTwo();
} else {
fireOne();
}
}
}

View File

@ -0,0 +1,26 @@
name: example
description: Simple project to showcase the usage of flame_audio
publish_to: 'none'
version: 1.0.0+1
environment:
sdk: ">=2.12.0 <3.0.0"
dependencies:
flame: 1.0.0-releasecandidate.11
flame_audio:
path: ../
flutter:
sdk: flutter
dev_dependencies:
dart_code_metrics: ^3.2.2
flutter:
uses-material-design: false
assets:
- assets/audio/music/bg_music.ogg
- assets/audio/sfx/fire_1.mp3
- assets/audio/sfx/fire_2.mp3

View File

@ -0,0 +1,101 @@
import 'dart:async';
import 'package:audioplayers/audioplayers.dart';
import 'package:synchronized/synchronized.dart';
typedef Stoppable = void Function();
/// An AudioPool is a provider of AudioPlayers that leaves them pre-loaded to minimize delays.
///
/// 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 = [];
final String sound;
final bool repeating;
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;
static Future<AudioPool> create(
String sound, {
bool? repeating,
int? maxPlayers,
int? minPlayers = 1,
String? prefix,
}) async {
final instance = AudioPool._(
sound,
repeating: repeating,
maxPlayers: maxPlayers,
minPlayers: minPlayers,
prefix: prefix,
);
for (var i = 0; i < instance.minPlayers; i++) {
instance._availablePlayers.add(await instance._createNewAudioPlayer());
}
return instance;
}
Future<Stoppable> start({double volume = 1.0}) async {
return _lock.synchronized(() async {
if (_availablePlayers.isEmpty) {
_availablePlayers.add(await _createNewAudioPlayer());
}
final player = _availablePlayers.removeAt(0);
_currentPlayers[player.playerId] = player;
await player.setVolume(volume);
await player.resume();
late StreamSubscription<void> subscription;
void stop() {
_lock.synchronized(() async {
final p = _currentPlayers.remove(player.playerId);
if (p != null) {
subscription.cancel();
await p.stop();
if (_availablePlayers.length >= maxPlayers) {
await p.release();
} else {
_availablePlayers.add(p);
}
}
});
}
subscription = player.onPlayerCompletion.listen((_) {
if (repeating) {
player.resume();
} else {
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);
return player;
}
}

View File

@ -0,0 +1,123 @@
import 'dart:io';
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/widgets.dart';
/// The looping background music class.
///
/// This class helps with looping background music management that reacts to
/// application lifecycle state changes. On construction, the instance is added
/// as an observer to the [WidgetsBinding] instance. A [dispose] function is
/// provided in case this functionality needs to be unloaded but the app needs
/// to keep running.
class Bgm extends WidgetsBindingObserver {
bool _isRegistered = false;
late AudioCache audioCache;
AudioPlayer? audioPlayer;
bool isPlaying = false;
Bgm({AudioCache? audioCache}) : audioCache = audioCache ?? AudioCache();
/// Registers a [WidgetsBinding] observer.
///
/// This must be called for auto-pause and resume to work properly.
void initialize() {
if (_isRegistered) {
return;
}
_isRegistered = true;
WidgetsBinding.instance?.addObserver(this);
}
/// Dispose the [WidgetsBinding] observer.
void dispose() {
if (!_isRegistered) {
return;
}
WidgetsBinding.instance?.removeObserver(this);
_isRegistered = false;
}
/// Plays and loops a background music file specified by [filename].
///
/// The volume can be specified in the optional named parameter [volume]
/// where `0` means off and `1` means max.
///
/// It is safe to call this function even when a current BGM track is
/// playing.
Future<void> play(String filename, {double volume = 1}) async {
final currentPlayer = audioPlayer;
if (currentPlayer != null && currentPlayer.state != PlayerState.STOPPED) {
currentPlayer.stop();
}
isPlaying = true;
audioPlayer = await audioCache.loop(filename, volume: volume);
}
/// Stops the currently playing background music track (if any).
Future<void> stop() async {
isPlaying = false;
if (audioPlayer != null) {
await audioPlayer!.stop();
}
}
/// Resumes the currently played (but resumed) background music.
Future<void> resume() async {
if (audioPlayer != null) {
isPlaying = true;
await audioPlayer!.resume();
}
}
/// Pauses the background music without unloading or resetting the audio
/// player.
Future<void> pause() async {
if (audioPlayer != null) {
isPlaying = false;
await audioPlayer!.pause();
}
}
/// Pre-fetch an audio and store it in the cache.
///
/// Alias of `audioCache.load();`.
Future<Uri> load(String file) => audioCache.load(file);
/// Pre-fetch an audio and store it in the cache.
///
/// Alias of `audioCache.loadAsFile();`.
Future<File> loadAsFile(String file) => audioCache.loadAsFile(file);
/// Pre-fetch a list of audios and store them in the cache.
///
/// Alias of `audioCache.loadAll();`.
Future<List<Uri>> loadAll(List<String> files) => audioCache.loadAll(files);
/// Clears the file in the cache.
///
/// Alias of `audioCache.clear();`.
void clear(Uri file) => audioCache.clear(file);
/// Clears all the audios in the cache.
///
/// Alias of `audioCache.clearAll();`.
void clearAll() => audioCache.clearAll();
/// Handler for AppLifecycleState changes.
///
/// This function handles the automatic pause and resume when the app
/// lifecycle state changes. There is NO NEED to call this function directly
/// directly.
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
if (isPlaying && audioPlayer?.state == PlayerState.PAUSED) {
audioPlayer?.resume();
}
} else {
audioPlayer?.pause();
}
}
}

View File

@ -0,0 +1,43 @@
import 'package:audioplayers/audioplayers.dart';
import 'bgm.dart';
/// This utility class holds static references to some global audio objects.
///
/// You can use as a helper to very simply play a sound or a background music.
/// Alternatively you can create your own instances and control them yourself.
class FlameAudio {
/// Access a shared instance of the [AudioCache] class.
static AudioCache audioCache = AudioCache(prefix: 'assets/audio/');
/// Plays a single run of the given [file], with a given [volume].
static Future<AudioPlayer> play(String file, {double volume = 1.0}) {
return audioCache.play(file, volume: volume, mode: PlayerMode.LOW_LATENCY);
}
/// Plays, and keep looping the given [file]
static Future<AudioPlayer> loop(String file, {double volume = 1.0}) {
return audioCache.loop(file, volume: volume, mode: PlayerMode.LOW_LATENCY);
}
/// Plays a single run of the given file [file]
/// This method supports long audio files
static Future<AudioPlayer> playLongAudio(String file, {double volume = 1.0}) {
return audioCache.play(file, volume: volume);
}
/// Plays, and keep looping the given [file]
/// This method supports long audio files
///
/// NOTE: Length audio files on Android have an audio gap between loop
/// iterations, this happens due to limitations on Android's native media
/// player features, if you need a gapless loop, prefer the loop method
static Future<AudioPlayer> loopLongAudio(String file, {double volume = 1.0}) {
return audioCache.loop(file, volume: volume);
}
/// Access a shared instance of the [Bgm] class.
///
/// This will use the same global audio cache from [FlameAudio].
static late final Bgm bgm = Bgm(audioCache: audioCache);
}

View File

@ -0,0 +1,22 @@
name: flame_audio
description: Audio support for the Flame game engine. This containst all audio related code will live in the future, using the audioplayers package.
version: 0.1.0-rc5
homepage: https://github.com/flame-engine/flame_audio
environment:
sdk: ">=2.12.0 <3.0.0"
flutter: ">=1.17.0"
dependencies:
flame: '>=1.0.0-releasecandidate.11'
audioplayers: ^0.19.0
synchronized: ^3.0.0
flutter:
sdk: flutter
dev_dependencies:
dart_code_metrics: ^3.2.2
dartdoc: ^0.42.0
flutter_test:
sdk: flutter
test: ^1.9.4