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 2 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
void start() {
component.audio.backgroundMusic();
component.firstChild<Backboard>()?.waitingMode();
component.firstChild<CameraController>()?.focusOnGame();
component.overlays.remove(PinballGame.playButtonOverlay);

@ -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();
}

@ -17,6 +17,14 @@ typedef CreateAudioPool = Future<AudioPool> Function(
/// audio
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
/// 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<void> 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';
}

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

@ -29,6 +29,19 @@ abstract class _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 MockAudioCache extends Mock implements AudioCache {}

@ -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);
});
});
});
}

@ -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<CameraController>).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);
},
);
});
});
}

Loading…
Cancel
Save