mirror of https://github.com/flutter/pinball.git
feat: adds sound effects (#143)
* feat: adding placeholder sound effects * feat: adding sound effects * fix: lint * fix: adding pinball audio to mock * fix: testspull/146/head
parent
4e0f0ffe29
commit
c65dfb6001
@ -0,0 +1,19 @@
|
||||
name: pinball_audio
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "packages/pinball_audio/**"
|
||||
- ".github/workflows/pinball_audio.yaml"
|
||||
|
||||
pull_request:
|
||||
paths:
|
||||
- "packages/pinball_audio/**"
|
||||
- ".github/workflows/pinball_audio.yaml"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1
|
||||
with:
|
||||
working_directory: packages/pinball_audio
|
||||
coverage_excludes: "lib/gen/*.dart"
|
@ -0,0 +1,39 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# VSCode related
|
||||
.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
|
@ -0,0 +1,11 @@
|
||||
# pinball_audio
|
||||
|
||||
[![style: very good analysis][very_good_analysis_badge]][very_good_analysis_link]
|
||||
[![License: MIT][license_badge]][license_link]
|
||||
|
||||
Package with the sound manager for the pinball game
|
||||
|
||||
[license_badge]: https://img.shields.io/badge/license-MIT-blue.svg
|
||||
[license_link]: https://opensource.org/licenses/MIT
|
||||
[very_good_analysis_badge]: https://img.shields.io/badge/style-very_good_analysis-B22C89.svg
|
||||
[very_good_analysis_link]: https://pub.dev/packages/very_good_analysis
|
@ -0,0 +1,4 @@
|
||||
include: package:very_good_analysis/analysis_options.2.4.0.yaml
|
||||
analyzer:
|
||||
exclude:
|
||||
- lib/**/*.gen.dart
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1,69 @@
|
||||
/// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
/// *****************************************************
|
||||
/// FlutterGen
|
||||
/// *****************************************************
|
||||
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class $AssetsSfxGen {
|
||||
const $AssetsSfxGen();
|
||||
|
||||
String get google => 'assets/sfx/google.ogg';
|
||||
String get plim => 'assets/sfx/plim.ogg';
|
||||
}
|
||||
|
||||
class Assets {
|
||||
Assets._();
|
||||
|
||||
static const $AssetsSfxGen sfx = $AssetsSfxGen();
|
||||
}
|
||||
|
||||
class AssetGenImage extends AssetImage {
|
||||
const AssetGenImage(String assetName)
|
||||
: super(assetName, package: 'pinball_audio');
|
||||
|
||||
Image image({
|
||||
Key? key,
|
||||
ImageFrameBuilder? frameBuilder,
|
||||
ImageLoadingBuilder? loadingBuilder,
|
||||
ImageErrorWidgetBuilder? errorBuilder,
|
||||
String? semanticLabel,
|
||||
bool excludeFromSemantics = false,
|
||||
double? width,
|
||||
double? height,
|
||||
Color? color,
|
||||
BlendMode? colorBlendMode,
|
||||
BoxFit? fit,
|
||||
AlignmentGeometry alignment = Alignment.center,
|
||||
ImageRepeat repeat = ImageRepeat.noRepeat,
|
||||
Rect? centerSlice,
|
||||
bool matchTextDirection = false,
|
||||
bool gaplessPlayback = false,
|
||||
bool isAntiAlias = false,
|
||||
FilterQuality filterQuality = FilterQuality.low,
|
||||
}) {
|
||||
return Image(
|
||||
key: key,
|
||||
image: this,
|
||||
frameBuilder: frameBuilder,
|
||||
loadingBuilder: loadingBuilder,
|
||||
errorBuilder: errorBuilder,
|
||||
semanticLabel: semanticLabel,
|
||||
excludeFromSemantics: excludeFromSemantics,
|
||||
width: width,
|
||||
height: height,
|
||||
color: color,
|
||||
colorBlendMode: colorBlendMode,
|
||||
fit: fit,
|
||||
alignment: alignment,
|
||||
repeat: repeat,
|
||||
centerSlice: centerSlice,
|
||||
matchTextDirection: matchTextDirection,
|
||||
gaplessPlayback: gaplessPlayback,
|
||||
isAntiAlias: isAntiAlias,
|
||||
filterQuality: filterQuality,
|
||||
);
|
||||
}
|
||||
|
||||
String get path => assetName;
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
library pinball_audio;
|
||||
|
||||
export 'src/pinball_audio.dart';
|
@ -0,0 +1,71 @@
|
||||
import 'package:audioplayers/audioplayers.dart';
|
||||
import 'package:flame_audio/audio_pool.dart';
|
||||
import 'package:flame_audio/flame_audio.dart';
|
||||
import 'package:pinball_audio/gen/assets.gen.dart';
|
||||
|
||||
/// Function that defines the contract of the creation
|
||||
/// of an [AudioPool]
|
||||
typedef CreateAudioPool = Future<AudioPool> Function(
|
||||
String sound, {
|
||||
bool? repeating,
|
||||
int? maxPlayers,
|
||||
int? minPlayers,
|
||||
String? prefix,
|
||||
});
|
||||
|
||||
/// Function that defines the contract for playing a single
|
||||
/// audio
|
||||
typedef PlaySingleAudio = Future<void> Function(String);
|
||||
|
||||
/// Function that defines the contract for configuring
|
||||
/// an [AudioCache] instance
|
||||
typedef ConfigureAudioCache = void Function(AudioCache);
|
||||
|
||||
/// {@template pinball_audio}
|
||||
/// Sound manager for the pinball game
|
||||
/// {@endtemplate}
|
||||
class PinballAudio {
|
||||
/// {@macro pinball_audio}
|
||||
PinballAudio({
|
||||
CreateAudioPool? createAudioPool,
|
||||
PlaySingleAudio? playSingleAudio,
|
||||
ConfigureAudioCache? configureAudioCache,
|
||||
}) : _createAudioPool = createAudioPool ?? AudioPool.create,
|
||||
_playSingleAudio = playSingleAudio ?? FlameAudio.audioCache.play,
|
||||
_configureAudioCache = configureAudioCache ??
|
||||
((AudioCache a) {
|
||||
a.prefix = '';
|
||||
});
|
||||
|
||||
final CreateAudioPool _createAudioPool;
|
||||
|
||||
final PlaySingleAudio _playSingleAudio;
|
||||
|
||||
final ConfigureAudioCache _configureAudioCache;
|
||||
|
||||
late AudioPool _scorePool;
|
||||
|
||||
/// Loads the sounds effects into the memory
|
||||
Future<void> load() async {
|
||||
_configureAudioCache(FlameAudio.audioCache);
|
||||
_scorePool = await _createAudioPool(
|
||||
_prefixFile(Assets.sfx.plim),
|
||||
maxPlayers: 4,
|
||||
prefix: '',
|
||||
);
|
||||
}
|
||||
|
||||
/// Plays the basic score sound
|
||||
void score() {
|
||||
_scorePool.start();
|
||||
}
|
||||
|
||||
/// Plays the google word bonus
|
||||
void googleBonus() {
|
||||
_playSingleAudio(_prefixFile(Assets.sfx.google));
|
||||
}
|
||||
|
||||
String _prefixFile(String file) {
|
||||
return 'packages/pinball_audio/$file';
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
name: pinball_audio
|
||||
description: Package with the sound manager for the pinball game
|
||||
version: 1.0.0+1
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ">=2.16.0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
audioplayers: ^0.20.1
|
||||
flame_audio: ^1.0.1
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
mocktail: ^0.3.0
|
||||
very_good_analysis: ^2.4.0
|
||||
|
||||
flutter_gen:
|
||||
line_length: 80
|
||||
assets:
|
||||
package_parameter_enabled: true
|
||||
|
||||
flutter:
|
||||
assets:
|
||||
- assets/sfx/
|
@ -0,0 +1 @@
|
||||
export 'mocks.dart';
|
@ -0,0 +1,34 @@
|
||||
// ignore_for_file: one_member_abstracts
|
||||
|
||||
import 'package:audioplayers/audioplayers.dart';
|
||||
import 'package:flame_audio/audio_pool.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
abstract class _CreateAudioPoolStub {
|
||||
Future<AudioPool> onCall(
|
||||
String sound, {
|
||||
bool? repeating,
|
||||
int? maxPlayers,
|
||||
int? minPlayers,
|
||||
String? prefix,
|
||||
});
|
||||
}
|
||||
|
||||
class CreateAudioPoolStub extends Mock implements _CreateAudioPoolStub {}
|
||||
|
||||
abstract class _ConfigureAudioCacheStub {
|
||||
void onCall(AudioCache cache);
|
||||
}
|
||||
|
||||
class ConfigureAudioCacheStub extends Mock implements _ConfigureAudioCacheStub {
|
||||
}
|
||||
|
||||
abstract class _PlaySingleAudioStub {
|
||||
Future<void> onCall(String url);
|
||||
}
|
||||
|
||||
class PlaySingleAudioStub extends Mock implements _PlaySingleAudioStub {}
|
||||
|
||||
class MockAudioPool extends Mock implements AudioPool {}
|
||||
|
||||
class MockAudioCache extends Mock implements AudioCache {}
|
@ -0,0 +1,110 @@
|
||||
// ignore_for_file: prefer_const_constructors
|
||||
import 'package:flame_audio/flame_audio.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:pinball_audio/gen/assets.gen.dart';
|
||||
import 'package:pinball_audio/pinball_audio.dart';
|
||||
|
||||
import '../helpers/helpers.dart';
|
||||
|
||||
void main() {
|
||||
group('PinballAudio', () {
|
||||
test('can be instantiated', () {
|
||||
expect(PinballAudio(), isNotNull);
|
||||
});
|
||||
|
||||
late CreateAudioPoolStub createAudioPool;
|
||||
late ConfigureAudioCacheStub configureAudioCache;
|
||||
late PlaySingleAudioStub playSingleAudio;
|
||||
late PinballAudio audio;
|
||||
|
||||
setUpAll(() {
|
||||
registerFallbackValue(MockAudioCache());
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
createAudioPool = CreateAudioPoolStub();
|
||||
when(
|
||||
() => createAudioPool.onCall(
|
||||
any(),
|
||||
maxPlayers: any(named: 'maxPlayers'),
|
||||
prefix: any(named: 'prefix'),
|
||||
),
|
||||
).thenAnswer((_) async => MockAudioPool());
|
||||
|
||||
configureAudioCache = ConfigureAudioCacheStub();
|
||||
when(() => configureAudioCache.onCall(any())).thenAnswer((_) {});
|
||||
|
||||
playSingleAudio = PlaySingleAudioStub();
|
||||
when(() => playSingleAudio.onCall(any())).thenAnswer((_) async {});
|
||||
|
||||
audio = PinballAudio(
|
||||
configureAudioCache: configureAudioCache.onCall,
|
||||
createAudioPool: createAudioPool.onCall,
|
||||
playSingleAudio: playSingleAudio.onCall,
|
||||
);
|
||||
});
|
||||
|
||||
group('load', () {
|
||||
test('creates the score pool', () async {
|
||||
await audio.load();
|
||||
|
||||
verify(
|
||||
() => createAudioPool.onCall(
|
||||
'packages/pinball_audio/${Assets.sfx.plim}',
|
||||
maxPlayers: 4,
|
||||
prefix: '',
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
test('configures the audio cache instance', () async {
|
||||
await audio.load();
|
||||
|
||||
verify(() => configureAudioCache.onCall(FlameAudio.audioCache))
|
||||
.called(1);
|
||||
});
|
||||
|
||||
test('sets the correct prefix', () async {
|
||||
audio = PinballAudio(
|
||||
createAudioPool: createAudioPool.onCall,
|
||||
playSingleAudio: playSingleAudio.onCall,
|
||||
);
|
||||
await audio.load();
|
||||
|
||||
expect(FlameAudio.audioCache.prefix, equals(''));
|
||||
});
|
||||
});
|
||||
|
||||
group('score', () {
|
||||
test('plays the score sound pool', () async {
|
||||
final audioPool = MockAudioPool();
|
||||
when(audioPool.start).thenAnswer((_) async => () {});
|
||||
when(
|
||||
() => createAudioPool.onCall(
|
||||
any(),
|
||||
maxPlayers: any(named: 'maxPlayers'),
|
||||
prefix: any(named: 'prefix'),
|
||||
),
|
||||
).thenAnswer((_) async => audioPool);
|
||||
|
||||
await audio.load();
|
||||
audio.score();
|
||||
|
||||
verify(audioPool.start).called(1);
|
||||
});
|
||||
});
|
||||
|
||||
group('googleBonus', () {
|
||||
test('plays the correct file', () async {
|
||||
await audio.load();
|
||||
audio.googleBonus();
|
||||
|
||||
verify(
|
||||
() => playSingleAudio
|
||||
.onCall('packages/pinball_audio/${Assets.sfx.google}'),
|
||||
).called(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Reference in new issue