// Copyright 2022, the Flutter project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. import 'package:flutter/widgets.dart'; import 'package:logging/logging.dart'; import 'package:puzzleweave/settings/settings_controller.dart'; import 'package:jc_audio_player/jc_audio_player.dart'; enum SfxType { drop, click, tap, pop, appear, alert, star, success, flip, card, cardShort, panstart, panend, panend2 } //AudioLogger.logLevel = AudioLogLevel.info; /// Allows playing music and sound. A facade to `package:audioplayers`. class JcAudioController { static final _log = Logger('JcAudioController'); SettingsController? _settings; ValueNotifier? _lifecycleNotifier; final JcAudioPlayer _audioPlayer = JcAudioPlayer(); JcAudioController() { _audioPlayer.addMusic(1, 'assets/audio/bgm/canon.mp3'); _audioPlayer.addSound(SfxType.drop.index, 'assets/audio/sfx/button_click8.mp3'); _audioPlayer.addSound(SfxType.click.index, 'assets/audio/sfx/click.mp3'); _audioPlayer.addSound(SfxType.tap.index, 'assets/audio/sfx/tap.mp3'); _audioPlayer.addSound(SfxType.pop.index, 'assets/audio/sfx/pop.mp3'); _audioPlayer.addSound(SfxType.appear.index, 'assets/audio/sfx/appear.mp3'); _audioPlayer.addSound(SfxType.alert.index, 'assets/audio/sfx/alert.mp3'); _audioPlayer.addSound(SfxType.star.index, 'assets/audio/sfx/star.mp3'); _audioPlayer.addSound(SfxType.success.index, 'assets/audio/sfx/win.mp3'); _audioPlayer.addSound(SfxType.card.index, 'assets/audio/sfx/card.mp3'); _audioPlayer.addSound(SfxType.cardShort.index, 'assets/audio/sfx/card2.mp3'); _audioPlayer.addSound(SfxType.flip.index, 'assets/audio/sfx/flip.mp3'); _audioPlayer.addSound(SfxType.panstart.index, 'assets/audio/sfx/pan_start.mp3'); _audioPlayer.addSound(SfxType.panend.index, 'assets/audio/sfx/pan_end.mp3'); _audioPlayer.addSound(SfxType.panend2.index, 'assets/audio/sfx/pan_end2.mp3'); } void initialize() {} void dispose() { _lifecycleNotifier?.removeListener(_handleAppLifecycle); stopMusic(); stopSound(); } /// Enables the [AudioController] to listen to [AppLifecycleState] events, /// and therefore do things like stopping playback when the game /// goes into the background. void attachLifecycleNotifier(ValueNotifier lifecycleNotifier) { _lifecycleNotifier?.removeListener(_handleAppLifecycle); lifecycleNotifier.addListener(_handleAppLifecycle); _lifecycleNotifier = lifecycleNotifier; } /// Enables the [AudioController] to track changes to settings. /// Namely, when any of [SettingsController.muted], /// [SettingsController.musicOn] or [SettingsController.soundsOn] changes, /// the audio controller will act accordingly. void attachSettings(SettingsController settingsController) { if (_settings == settingsController) { // Already attached to this instance. Nothing to do. return; } // Remove handlers from the old settings controller if present final oldSettings = _settings; if (oldSettings != null) { oldSettings.music.removeListener(_musicOnHandler); oldSettings.sound.removeListener(_soundOnHandler); } _settings = settingsController; // Add handlers to the new settings controller settingsController.music.addListener(_musicOnHandler); settingsController.sound.addListener(_soundOnHandler); } /// Plays a single sound effect, defined by [type]. /// /// The controller will ignore this call when the attached settings' /// [SettingsController.soundsOn] is `false`. void playSfx(SfxType type, {Duration? duration}) async { final soundsOn = _settings?.sound.value ?? false; if (!soundsOn) { _log.info(() => 'Ignoring playing sound ($type) because sounds are turned off.'); return; } _audioPlayer.playSound(type.index); if (duration != null) { Future.delayed(duration, () => _audioPlayer.stopSound()); } } void _handleAppLifecycle() { _log.info(_lifecycleNotifier!.value); switch (_lifecycleNotifier!.value) { case AppLifecycleState.paused: case AppLifecycleState.detached: case AppLifecycleState.hidden: case AppLifecycleState.inactive: pauseMusic(); break; case AppLifecycleState.resumed: resumeMusic(); break; } } void _musicOnHandler() { if (_settings!.music.value) { // Music got turned on. // _resumeMusic(); startMusic(); } else { // Music got turned off. stopMusic(); } } void _soundOnHandler() {} void startMusic() { _log.info('starting music'); final musicOn = _settings?.music.value ?? false; if (!musicOn) { _log.info(() => 'Ignoring playing music because music are turned off.'); return; } _audioPlayer.playMusic(1, volume: 0.3); } void stopMusic() { _log.info('Stopping music'); _audioPlayer.stopMusic(); } void pauseMusic() { _log.info('pause music'); _audioPlayer.pauseMusic(); } Future resumeMusic() async { _log.info('Resuming music'); final musicOn = _settings?.music.value ?? false; if (musicOn) { _audioPlayer.resumeMusic(); } } void stopSound() { _audioPlayer.stopSound(); } }