feat: background music (#299)

* feat: adding background music

* feat: add missing test

* fix: removing uneeded comment

* converting music to OGG

* Apply suggestions from code review

Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com>

Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com>
pull/304/head
Erick 3 years ago committed by GitHub
parent 2a7fa9650f
commit 6cca3b84b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -39,6 +39,7 @@ class GameFlowController extends ComponentController<PinballGame>
/// Puts the game on a playing state /// Puts the game on a playing state
void start() { void start() {
component.audio.backgroundMusic();
component.firstChild<Backboard>()?.waitingMode(); component.firstChild<Backboard>()?.waitingMode();
component.firstChild<CameraController>()?.focusOnGame(); component.firstChild<CameraController>()?.focusOnGame();
component.overlays.remove(PinballGame.playButtonOverlay); component.overlays.remove(PinballGame.playButtonOverlay);

@ -5,6 +5,12 @@
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
class $AssetsMusicGen {
const $AssetsMusicGen();
String get background => 'assets/music/background.ogg';
}
class $AssetsSfxGen { class $AssetsSfxGen {
const $AssetsSfxGen(); const $AssetsSfxGen();
@ -15,6 +21,7 @@ class $AssetsSfxGen {
class Assets { class Assets {
Assets._(); Assets._();
static const $AssetsMusicGen music = $AssetsMusicGen();
static const $AssetsSfxGen sfx = $AssetsSfxGen(); static const $AssetsSfxGen sfx = $AssetsSfxGen();
} }

@ -17,6 +17,14 @@ typedef CreateAudioPool = Future<AudioPool> Function(
/// audio /// audio
typedef PlaySingleAudio = Future<void> Function(String); typedef PlaySingleAudio = Future<void> Function(String);
/// Function that defines the contract for looping a single
/// audio
typedef LoopSingleAudio = Future<void> Function(String);
/// Function that defines the contract for pre fetching an
/// audio
typedef PreCacheSingleAudio = Future<void> Function(String);
/// Function that defines the contract for configuring /// Function that defines the contract for configuring
/// an [AudioCache] instance /// an [AudioCache] instance
typedef ConfigureAudioCache = void Function(AudioCache); typedef ConfigureAudioCache = void Function(AudioCache);
@ -29,9 +37,14 @@ class PinballAudio {
PinballAudio({ PinballAudio({
CreateAudioPool? createAudioPool, CreateAudioPool? createAudioPool,
PlaySingleAudio? playSingleAudio, PlaySingleAudio? playSingleAudio,
LoopSingleAudio? loopSingleAudio,
PreCacheSingleAudio? preCacheSingleAudio,
ConfigureAudioCache? configureAudioCache, ConfigureAudioCache? configureAudioCache,
}) : _createAudioPool = createAudioPool ?? AudioPool.create, }) : _createAudioPool = createAudioPool ?? AudioPool.create,
_playSingleAudio = playSingleAudio ?? FlameAudio.audioCache.play, _playSingleAudio = playSingleAudio ?? FlameAudio.audioCache.play,
_loopSingleAudio = loopSingleAudio ?? FlameAudio.audioCache.loop,
_preCacheSingleAudio =
preCacheSingleAudio ?? FlameAudio.audioCache.load,
_configureAudioCache = configureAudioCache ?? _configureAudioCache = configureAudioCache ??
((AudioCache a) { ((AudioCache a) {
a.prefix = ''; a.prefix = '';
@ -41,6 +54,10 @@ class PinballAudio {
final PlaySingleAudio _playSingleAudio; final PlaySingleAudio _playSingleAudio;
final LoopSingleAudio _loopSingleAudio;
final PreCacheSingleAudio _preCacheSingleAudio;
final ConfigureAudioCache _configureAudioCache; final ConfigureAudioCache _configureAudioCache;
late AudioPool _scorePool; late AudioPool _scorePool;
@ -48,11 +65,17 @@ class PinballAudio {
/// Loads the sounds effects into the memory /// Loads the sounds effects into the memory
Future<void> load() async { Future<void> load() async {
_configureAudioCache(FlameAudio.audioCache); _configureAudioCache(FlameAudio.audioCache);
_scorePool = await _createAudioPool( _scorePool = await _createAudioPool(
_prefixFile(Assets.sfx.plim), _prefixFile(Assets.sfx.plim),
maxPlayers: 4, maxPlayers: 4,
prefix: '', prefix: '',
); );
await Future.wait([
_preCacheSingleAudio(_prefixFile(Assets.sfx.google)),
_preCacheSingleAudio(_prefixFile(Assets.music.background)),
]);
} }
/// Plays the basic score sound /// Plays the basic score sound
@ -65,6 +88,11 @@ class PinballAudio {
_playSingleAudio(_prefixFile(Assets.sfx.google)); _playSingleAudio(_prefixFile(Assets.sfx.google));
} }
/// Plays the background music
void backgroundMusic() {
_loopSingleAudio(_prefixFile(Assets.music.background));
}
String _prefixFile(String file) { String _prefixFile(String file) {
return 'packages/pinball_audio/$file'; return 'packages/pinball_audio/$file';
} }

@ -26,3 +26,4 @@ flutter_gen:
flutter: flutter:
assets: assets:
- assets/sfx/ - assets/sfx/
- assets/music/

@ -29,6 +29,19 @@ abstract class _PlaySingleAudioStub {
class PlaySingleAudioStub extends Mock implements _PlaySingleAudioStub {} class PlaySingleAudioStub extends Mock implements _PlaySingleAudioStub {}
abstract class _LoopSingleAudioStub {
Future<void> onCall(String url);
}
class LoopSingleAudioStub extends Mock implements _LoopSingleAudioStub {}
abstract class _PreCacheSingleAudioStub {
Future<void> onCall(String url);
}
class PreCacheSingleAudioStub extends Mock implements _PreCacheSingleAudioStub {
}
class MockAudioPool extends Mock implements AudioPool {} class MockAudioPool extends Mock implements AudioPool {}
class MockAudioCache extends Mock implements AudioCache {} class MockAudioCache extends Mock implements AudioCache {}

@ -16,6 +16,8 @@ void main() {
late CreateAudioPoolStub createAudioPool; late CreateAudioPoolStub createAudioPool;
late ConfigureAudioCacheStub configureAudioCache; late ConfigureAudioCacheStub configureAudioCache;
late PlaySingleAudioStub playSingleAudio; late PlaySingleAudioStub playSingleAudio;
late LoopSingleAudioStub loopSingleAudio;
late PreCacheSingleAudioStub preCacheSingleAudio;
late PinballAudio audio; late PinballAudio audio;
setUpAll(() { setUpAll(() {
@ -38,10 +40,18 @@ void main() {
playSingleAudio = PlaySingleAudioStub(); playSingleAudio = PlaySingleAudioStub();
when(() => playSingleAudio.onCall(any())).thenAnswer((_) async {}); when(() => playSingleAudio.onCall(any())).thenAnswer((_) async {});
loopSingleAudio = LoopSingleAudioStub();
when(() => loopSingleAudio.onCall(any())).thenAnswer((_) async {});
preCacheSingleAudio = PreCacheSingleAudioStub();
when(() => preCacheSingleAudio.onCall(any())).thenAnswer((_) async {});
audio = PinballAudio( audio = PinballAudio(
configureAudioCache: configureAudioCache.onCall, configureAudioCache: configureAudioCache.onCall,
createAudioPool: createAudioPool.onCall, createAudioPool: createAudioPool.onCall,
playSingleAudio: playSingleAudio.onCall, playSingleAudio: playSingleAudio.onCall,
loopSingleAudio: loopSingleAudio.onCall,
preCacheSingleAudio: preCacheSingleAudio.onCall,
); );
}); });
@ -69,11 +79,25 @@ void main() {
audio = PinballAudio( audio = PinballAudio(
createAudioPool: createAudioPool.onCall, createAudioPool: createAudioPool.onCall,
playSingleAudio: playSingleAudio.onCall, playSingleAudio: playSingleAudio.onCall,
preCacheSingleAudio: preCacheSingleAudio.onCall,
); );
await audio.load(); await audio.load();
expect(FlameAudio.audioCache.prefix, equals('')); expect(FlameAudio.audioCache.prefix, equals(''));
}); });
test('pre cache the assets', () async {
await audio.load();
verify(
() => preCacheSingleAudio
.onCall('packages/pinball_audio/assets/sfx/google.ogg'),
).called(1);
verify(
() => preCacheSingleAudio
.onCall('packages/pinball_audio/assets/music/background.ogg'),
).called(1);
});
}); });
group('score', () { group('score', () {
@ -106,5 +130,17 @@ void main() {
).called(1); ).called(1);
}); });
}); });
group('backgroundMusic', () {
test('plays the correct file', () async {
await audio.load();
audio.backgroundMusic();
verify(
() => loopSingleAudio
.onCall('packages/pinball_audio/${Assets.music.background}'),
).called(1);
});
});
}); });
} }

@ -4,6 +4,7 @@ import 'package:flame/game.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
import 'package:pinball/game/game.dart'; import 'package:pinball/game/game.dart';
import 'package:pinball_audio/pinball_audio.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_theme/pinball_theme.dart'; import 'package:pinball_theme/pinball_theme.dart';
@ -33,6 +34,7 @@ void main() {
late Backboard backboard; late Backboard backboard;
late CameraController cameraController; late CameraController cameraController;
late GameFlowController gameFlowController; late GameFlowController gameFlowController;
late PinballAudio pinballAudio;
late ActiveOverlaysNotifier overlays; late ActiveOverlaysNotifier overlays;
setUp(() { setUp(() {
@ -41,6 +43,7 @@ void main() {
cameraController = MockCameraController(); cameraController = MockCameraController();
gameFlowController = GameFlowController(game); gameFlowController = GameFlowController(game);
overlays = MockActiveOverlaysNotifier(); overlays = MockActiveOverlaysNotifier();
pinballAudio = MockPinballAudio();
when( when(
() => backboard.gameOverMode( () => backboard.gameOverMode(
@ -59,6 +62,7 @@ void main() {
when(game.firstChild<CameraController>).thenReturn(cameraController); when(game.firstChild<CameraController>).thenReturn(cameraController);
when(() => game.overlays).thenReturn(overlays); when(() => game.overlays).thenReturn(overlays);
when(() => game.characterTheme).thenReturn(DashTheme()); when(() => game.characterTheme).thenReturn(DashTheme());
when(() => game.audio).thenReturn(pinballAudio);
}); });
test( test(
@ -95,6 +99,15 @@ void main() {
.called(1); .called(1);
}, },
); );
test(
'plays the background music on start',
() {
gameFlowController.onNewState(GameState.initial());
verify(pinballAudio.backgroundMusic).called(1);
},
);
}); });
}); });
} }

Loading…
Cancel
Save