diff --git a/lib/game/components/game_flow_controller.dart b/lib/game/components/game_flow_controller.dart index 48dd5518..edc65329 100644 --- a/lib/game/components/game_flow_controller.dart +++ b/lib/game/components/game_flow_controller.dart @@ -39,6 +39,7 @@ class GameFlowController extends ComponentController /// Puts the game on a playing state void start() { + component.audio.backgroundMusic(); component.firstChild()?.waitingMode(); component.firstChild()?.focusOnGame(); component.overlays.remove(PinballGame.playButtonOverlay); diff --git a/packages/pinball_audio/assets/music/background.ogg b/packages/pinball_audio/assets/music/background.ogg new file mode 100644 index 00000000..72482f6d Binary files /dev/null and b/packages/pinball_audio/assets/music/background.ogg differ diff --git a/packages/pinball_audio/lib/gen/assets.gen.dart b/packages/pinball_audio/lib/gen/assets.gen.dart index 3609b939..0e6e120e 100644 --- a/packages/pinball_audio/lib/gen/assets.gen.dart +++ b/packages/pinball_audio/lib/gen/assets.gen.dart @@ -5,6 +5,12 @@ import 'package:flutter/widgets.dart'; +class $AssetsMusicGen { + const $AssetsMusicGen(); + + String get background => 'assets/music/background.ogg'; +} + class $AssetsSfxGen { const $AssetsSfxGen(); @@ -15,6 +21,7 @@ class $AssetsSfxGen { class Assets { Assets._(); + static const $AssetsMusicGen music = $AssetsMusicGen(); static const $AssetsSfxGen sfx = $AssetsSfxGen(); } diff --git a/packages/pinball_audio/lib/src/pinball_audio.dart b/packages/pinball_audio/lib/src/pinball_audio.dart index b2875084..8bda14e5 100644 --- a/packages/pinball_audio/lib/src/pinball_audio.dart +++ b/packages/pinball_audio/lib/src/pinball_audio.dart @@ -17,6 +17,14 @@ typedef CreateAudioPool = Future Function( /// audio typedef PlaySingleAudio = Future Function(String); +/// Function that defines the contract for looping a single +/// audio +typedef LoopSingleAudio = Future Function(String); + +/// Function that defines the contract for pre fetching an +/// audio +typedef PreCacheSingleAudio = Future Function(String); + /// Function that defines the contract for configuring /// an [AudioCache] instance typedef ConfigureAudioCache = void Function(AudioCache); @@ -29,9 +37,14 @@ class PinballAudio { PinballAudio({ CreateAudioPool? createAudioPool, PlaySingleAudio? playSingleAudio, + LoopSingleAudio? loopSingleAudio, + PreCacheSingleAudio? preCacheSingleAudio, ConfigureAudioCache? configureAudioCache, }) : _createAudioPool = createAudioPool ?? AudioPool.create, _playSingleAudio = playSingleAudio ?? FlameAudio.audioCache.play, + _loopSingleAudio = loopSingleAudio ?? FlameAudio.audioCache.loop, + _preCacheSingleAudio = + preCacheSingleAudio ?? FlameAudio.audioCache.load, _configureAudioCache = configureAudioCache ?? ((AudioCache a) { a.prefix = ''; @@ -41,6 +54,10 @@ class PinballAudio { final PlaySingleAudio _playSingleAudio; + final LoopSingleAudio _loopSingleAudio; + + final PreCacheSingleAudio _preCacheSingleAudio; + final ConfigureAudioCache _configureAudioCache; late AudioPool _scorePool; @@ -48,11 +65,17 @@ class PinballAudio { /// Loads the sounds effects into the memory Future load() async { _configureAudioCache(FlameAudio.audioCache); + _scorePool = await _createAudioPool( _prefixFile(Assets.sfx.plim), maxPlayers: 4, prefix: '', ); + + await Future.wait([ + _preCacheSingleAudio(_prefixFile(Assets.sfx.google)), + _preCacheSingleAudio(_prefixFile(Assets.music.background)), + ]); } /// Plays the basic score sound @@ -65,6 +88,11 @@ class PinballAudio { _playSingleAudio(_prefixFile(Assets.sfx.google)); } + /// Plays the background music + void backgroundMusic() { + _loopSingleAudio(_prefixFile(Assets.music.background)); + } + String _prefixFile(String file) { return 'packages/pinball_audio/$file'; } diff --git a/packages/pinball_audio/pubspec.yaml b/packages/pinball_audio/pubspec.yaml index a34ba5b5..74713dfa 100644 --- a/packages/pinball_audio/pubspec.yaml +++ b/packages/pinball_audio/pubspec.yaml @@ -26,3 +26,4 @@ flutter_gen: flutter: assets: - assets/sfx/ + - assets/music/ diff --git a/packages/pinball_audio/test/helpers/mocks.dart b/packages/pinball_audio/test/helpers/mocks.dart index c80fe65b..62ea985b 100644 --- a/packages/pinball_audio/test/helpers/mocks.dart +++ b/packages/pinball_audio/test/helpers/mocks.dart @@ -29,6 +29,19 @@ abstract class _PlaySingleAudioStub { class PlaySingleAudioStub extends Mock implements _PlaySingleAudioStub {} +abstract class _LoopSingleAudioStub { + Future onCall(String url); +} + +class LoopSingleAudioStub extends Mock implements _LoopSingleAudioStub {} + +abstract class _PreCacheSingleAudioStub { + Future onCall(String url); +} + +class PreCacheSingleAudioStub extends Mock implements _PreCacheSingleAudioStub { +} + class MockAudioPool extends Mock implements AudioPool {} class MockAudioCache extends Mock implements AudioCache {} diff --git a/packages/pinball_audio/test/src/pinball_audio_test.dart b/packages/pinball_audio/test/src/pinball_audio_test.dart index 2efe9553..393934f0 100644 --- a/packages/pinball_audio/test/src/pinball_audio_test.dart +++ b/packages/pinball_audio/test/src/pinball_audio_test.dart @@ -16,6 +16,8 @@ void main() { late CreateAudioPoolStub createAudioPool; late ConfigureAudioCacheStub configureAudioCache; late PlaySingleAudioStub playSingleAudio; + late LoopSingleAudioStub loopSingleAudio; + late PreCacheSingleAudioStub preCacheSingleAudio; late PinballAudio audio; setUpAll(() { @@ -38,10 +40,18 @@ void main() { playSingleAudio = PlaySingleAudioStub(); when(() => playSingleAudio.onCall(any())).thenAnswer((_) async {}); + loopSingleAudio = LoopSingleAudioStub(); + when(() => loopSingleAudio.onCall(any())).thenAnswer((_) async {}); + + preCacheSingleAudio = PreCacheSingleAudioStub(); + when(() => preCacheSingleAudio.onCall(any())).thenAnswer((_) async {}); + audio = PinballAudio( configureAudioCache: configureAudioCache.onCall, createAudioPool: createAudioPool.onCall, playSingleAudio: playSingleAudio.onCall, + loopSingleAudio: loopSingleAudio.onCall, + preCacheSingleAudio: preCacheSingleAudio.onCall, ); }); @@ -69,11 +79,25 @@ void main() { audio = PinballAudio( createAudioPool: createAudioPool.onCall, playSingleAudio: playSingleAudio.onCall, + preCacheSingleAudio: preCacheSingleAudio.onCall, ); await audio.load(); 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', () { @@ -106,5 +130,17 @@ void main() { ).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); + }); + }); }); } diff --git a/test/game/components/game_flow_controller_test.dart b/test/game/components/game_flow_controller_test.dart index ef93892c..c3674146 100644 --- a/test/game/components/game_flow_controller_test.dart +++ b/test/game/components/game_flow_controller_test.dart @@ -4,6 +4,7 @@ import 'package:flame/game.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; import 'package:pinball/game/game.dart'; +import 'package:pinball_audio/pinball_audio.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_theme/pinball_theme.dart'; @@ -33,6 +34,7 @@ void main() { late Backboard backboard; late CameraController cameraController; late GameFlowController gameFlowController; + late PinballAudio pinballAudio; late ActiveOverlaysNotifier overlays; setUp(() { @@ -41,6 +43,7 @@ void main() { cameraController = MockCameraController(); gameFlowController = GameFlowController(game); overlays = MockActiveOverlaysNotifier(); + pinballAudio = MockPinballAudio(); when( () => backboard.gameOverMode( @@ -59,6 +62,7 @@ void main() { when(game.firstChild).thenReturn(cameraController); when(() => game.overlays).thenReturn(overlays); when(() => game.characterTheme).thenReturn(DashTheme()); + when(() => game.audio).thenReturn(pinballAudio); }); test( @@ -95,6 +99,15 @@ void main() { .called(1); }, ); + + test( + 'plays the background music on start', + () { + gameFlowController.onNewState(GameState.initial()); + + verify(pinballAudio.backgroundMusic).called(1); + }, + ); }); }); }