mirror of
https://github.com/flame-engine/flame.git
synced 2025-10-30 08:27:36 +08:00
Move flame_audio & update to work with current rc11 (#807)
This commit is contained in:
@ -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
75
packages/flame_audio/.gitignore
vendored
Normal 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
|
||||
24
packages/flame_audio/CHANGELOG.md
Normal file
24
packages/flame_audio/CHANGELOG.md
Normal 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.
|
||||
1
packages/flame_audio/LICENSE
Symbolic link
1
packages/flame_audio/LICENSE
Symbolic link
@ -0,0 +1 @@
|
||||
../../LICENSE
|
||||
28
packages/flame_audio/README.md
Normal file
28
packages/flame_audio/README.md
Normal 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).
|
||||
149
packages/flame_audio/analysis_options.yaml
Normal file
149
packages/flame_audio/analysis_options.yaml
Normal 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
51
packages/flame_audio/example/.gitignore
vendored
Normal 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
|
||||
10
packages/flame_audio/example/.metadata
Normal file
10
packages/flame_audio/example/.metadata
Normal 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
|
||||
6
packages/flame_audio/example/CREDITS.md
Normal file
6
packages/flame_audio/example/CREDITS.md
Normal 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
|
||||
3
packages/flame_audio/example/README.md
Normal file
3
packages/flame_audio/example/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# flame audio example
|
||||
|
||||
Simple project to showcase the usage of flame_audio
|
||||
BIN
packages/flame_audio/example/assets/audio/music/bg_music.ogg
Normal file
BIN
packages/flame_audio/example/assets/audio/music/bg_music.ogg
Normal file
Binary file not shown.
BIN
packages/flame_audio/example/assets/audio/sfx/fire_1.mp3
Normal file
BIN
packages/flame_audio/example/assets/audio/sfx/fire_1.mp3
Normal file
Binary file not shown.
BIN
packages/flame_audio/example/assets/audio/sfx/fire_2.mp3
Normal file
BIN
packages/flame_audio/example/assets/audio/sfx/fire_2.mp3
Normal file
Binary file not shown.
81
packages/flame_audio/example/lib/main.dart
Normal file
81
packages/flame_audio/example/lib/main.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
26
packages/flame_audio/example/pubspec.yaml
Normal file
26
packages/flame_audio/example/pubspec.yaml
Normal 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
|
||||
101
packages/flame_audio/lib/audio_pool.dart
Normal file
101
packages/flame_audio/lib/audio_pool.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
123
packages/flame_audio/lib/bgm.dart
Normal file
123
packages/flame_audio/lib/bgm.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
43
packages/flame_audio/lib/flame_audio.dart
Normal file
43
packages/flame_audio/lib/flame_audio.dart
Normal 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);
|
||||
}
|
||||
22
packages/flame_audio/pubspec.yaml
Normal file
22
packages/flame_audio/pubspec.yaml
Normal 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
|
||||
Reference in New Issue
Block a user