feat: Add Audio Gallery screen and integrate into Debug Tools menu
feat: Implement audio playback controls and audio management in the gallery refactor: Update audio engine interface to include stopAllAudio method Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"cmake.ignoreCMakeListsMissing": true,
|
"cmake.ignoreCMakeListsMissing": true,
|
||||||
"chat.tools.terminal.autoApprove": {
|
"chat.tools.terminal.autoApprove": {
|
||||||
"flutter": true
|
"flutter": true,
|
||||||
|
"dart": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,6 +22,9 @@ void main() async {
|
|||||||
|
|
||||||
runApp(
|
runApp(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
|
darkTheme: ThemeData.dark(useMaterial3: true),
|
||||||
|
theme: ThemeData.light(useMaterial3: true),
|
||||||
|
themeMode: ThemeMode.system,
|
||||||
home: wolf3d.availableGames.isEmpty
|
home: wolf3d.availableGames.isEmpty
|
||||||
? const _NoGameDataScreen()
|
? const _NoGameDataScreen()
|
||||||
: GameScreen(wolf3d: wolf3d),
|
: GameScreen(wolf3d: wolf3d),
|
||||||
|
|||||||
455
apps/wolf_3d_gui/lib/screens/audio_gallery.dart
Normal file
455
apps/wolf_3d_gui/lib/screens/audio_gallery.dart
Normal file
@@ -0,0 +1,455 @@
|
|||||||
|
/// Debug browser for SFX and music assets with playback controls.
|
||||||
|
library;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:wolf_3d_dart/wolf_3d_data_types.dart';
|
||||||
|
import 'package:wolf_3d_dart/wolf_3d_synth.dart';
|
||||||
|
import 'package:wolf_3d_flutter/wolf_3d_flutter.dart';
|
||||||
|
import 'package:wolf_3d_gui/screens/gallery_game_selector.dart';
|
||||||
|
|
||||||
|
class _AudioRow {
|
||||||
|
final int id;
|
||||||
|
final List<String> aliases;
|
||||||
|
|
||||||
|
const _AudioRow({required this.id, required this.aliases});
|
||||||
|
|
||||||
|
String get subtitle {
|
||||||
|
if (aliases.isEmpty) {
|
||||||
|
return 'No known alias';
|
||||||
|
}
|
||||||
|
return aliases.join(', ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Displays all decoded SFX and music tracks for the selected game data.
|
||||||
|
class AudioGallery extends StatefulWidget {
|
||||||
|
/// Shared app facade used to access game assets and the audio backend.
|
||||||
|
final Wolf3d wolf3d;
|
||||||
|
|
||||||
|
const AudioGallery({super.key, required this.wolf3d});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<AudioGallery> createState() => _AudioGalleryState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AudioGalleryState extends State<AudioGallery> {
|
||||||
|
late WolfensteinData _selectedGame;
|
||||||
|
int? _playingMusicTrackIndex;
|
||||||
|
|
||||||
|
int _gridColumnsForWidth(double width) {
|
||||||
|
if (width >= 1400) return 6;
|
||||||
|
if (width >= 1100) return 5;
|
||||||
|
if (width >= 800) return 4;
|
||||||
|
if (width >= 560) return 3;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const List<SfxKey> _knownSfxKeys = [
|
||||||
|
SfxKey.openDoor,
|
||||||
|
SfxKey.closeDoor,
|
||||||
|
SfxKey.pushWall,
|
||||||
|
SfxKey.knifeAttack,
|
||||||
|
SfxKey.pistolFire,
|
||||||
|
SfxKey.machineGunFire,
|
||||||
|
SfxKey.chainGunFire,
|
||||||
|
SfxKey.enemyFire,
|
||||||
|
SfxKey.getMachineGun,
|
||||||
|
SfxKey.getAmmo,
|
||||||
|
SfxKey.getChainGun,
|
||||||
|
SfxKey.healthSmall,
|
||||||
|
SfxKey.healthLarge,
|
||||||
|
SfxKey.treasure1,
|
||||||
|
SfxKey.treasure2,
|
||||||
|
SfxKey.treasure3,
|
||||||
|
SfxKey.treasure4,
|
||||||
|
SfxKey.extraLife,
|
||||||
|
SfxKey.guardHalt,
|
||||||
|
SfxKey.dogBark,
|
||||||
|
SfxKey.dogDeath,
|
||||||
|
SfxKey.dogAttack,
|
||||||
|
SfxKey.deathScream1,
|
||||||
|
SfxKey.deathScream2,
|
||||||
|
SfxKey.deathScream3,
|
||||||
|
SfxKey.ssAlert,
|
||||||
|
SfxKey.ssDeath,
|
||||||
|
SfxKey.bossActive,
|
||||||
|
SfxKey.hansGrosseDeath,
|
||||||
|
SfxKey.schabbs,
|
||||||
|
SfxKey.schabbsDeath,
|
||||||
|
SfxKey.hitlerGreeting,
|
||||||
|
SfxKey.hitlerDeath,
|
||||||
|
SfxKey.mechaSteps,
|
||||||
|
SfxKey.ottoAlert,
|
||||||
|
SfxKey.gretelDeath,
|
||||||
|
SfxKey.levelComplete,
|
||||||
|
SfxKey.endBonus1,
|
||||||
|
SfxKey.endBonus2,
|
||||||
|
SfxKey.noBonus,
|
||||||
|
SfxKey.percent100,
|
||||||
|
];
|
||||||
|
|
||||||
|
static const List<MusicKey> _knownMusicKeys = [
|
||||||
|
MusicKey.menuTheme,
|
||||||
|
MusicKey.level01,
|
||||||
|
MusicKey.level02,
|
||||||
|
MusicKey.level03,
|
||||||
|
MusicKey.level04,
|
||||||
|
MusicKey.level05,
|
||||||
|
MusicKey.level06,
|
||||||
|
MusicKey.level07,
|
||||||
|
MusicKey.level08,
|
||||||
|
MusicKey.level09,
|
||||||
|
MusicKey.level10,
|
||||||
|
MusicKey.level11,
|
||||||
|
MusicKey.level12,
|
||||||
|
MusicKey.level13,
|
||||||
|
MusicKey.level14,
|
||||||
|
MusicKey.level15,
|
||||||
|
MusicKey.level16,
|
||||||
|
MusicKey.level17,
|
||||||
|
MusicKey.level18,
|
||||||
|
MusicKey.level19,
|
||||||
|
MusicKey.level20,
|
||||||
|
];
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_selectedGame =
|
||||||
|
widget.wolf3d.maybeActiveGame ?? widget.wolf3d.availableGames.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
// Ensure debug playback does not continue after closing the gallery.
|
||||||
|
unawaited(widget.wolf3d.audio.stopAllAudio());
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<int, List<String>> _buildSfxAliases() {
|
||||||
|
final Map<int, Set<String>> aliasesById = {};
|
||||||
|
for (final key in _knownSfxKeys) {
|
||||||
|
final ref = _selectedGame.registry.sfx.resolve(key);
|
||||||
|
if (ref == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
aliasesById
|
||||||
|
.putIfAbsent(ref.slotIndex, () => <String>{})
|
||||||
|
.add(_readableKeyName(key.toString(), 'SfxKey('));
|
||||||
|
}
|
||||||
|
|
||||||
|
return aliasesById.map(
|
||||||
|
(id, aliases) => MapEntry(id, aliases.toList()..sort()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<int, List<String>> _buildMusicAliases() {
|
||||||
|
final Map<int, Set<String>> aliasesById = {};
|
||||||
|
for (final key in _knownMusicKeys) {
|
||||||
|
final route = _selectedGame.registry.music.resolve(key);
|
||||||
|
if (route == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
aliasesById
|
||||||
|
.putIfAbsent(route.trackIndex, () => <String>{})
|
||||||
|
.add(_readableKeyName(key.toString(), 'MusicKey('));
|
||||||
|
}
|
||||||
|
|
||||||
|
return aliasesById.map(
|
||||||
|
(id, aliases) => MapEntry(id, aliases.toList()..sort()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _readableKeyName(String raw, String prefix) {
|
||||||
|
final String trimmed = raw.startsWith(prefix) && raw.endsWith(')')
|
||||||
|
? raw.substring(prefix.length, raw.length - 1)
|
||||||
|
: raw;
|
||||||
|
if (trimmed.isEmpty) {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return trimmed.replaceAllMapped(
|
||||||
|
RegExp(r'([a-z0-9])([A-Z])'),
|
||||||
|
(match) => '${match.group(1)} ${match.group(2)}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _stopAllAudioPlayback() async {
|
||||||
|
await widget.wolf3d.audio.stopAllAudio();
|
||||||
|
if (!mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
_playingMusicTrackIndex = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _selectGame(WolfensteinData game) async {
|
||||||
|
if (identical(_selectedGame, game)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _stopAllAudioPlayback();
|
||||||
|
widget.wolf3d.setActiveGame(game);
|
||||||
|
if (!mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
_selectedGame = game;
|
||||||
|
_playingMusicTrackIndex = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _playSfx(int id) {
|
||||||
|
widget.wolf3d.audio.playSoundEffect(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _toggleMusic(int trackIndex) async {
|
||||||
|
if (_playingMusicTrackIndex == trackIndex) {
|
||||||
|
await _stopAllAudioPlayback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final engineAudio = widget.wolf3d.audio;
|
||||||
|
if (engineAudio is! WolfAudio) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trackIndex < 0 || trackIndex >= _selectedGame.music.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await engineAudio.playMusic(_selectedGame.music[trackIndex]);
|
||||||
|
if (!mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
_playingMusicTrackIndex = trackIndex;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return DefaultTabController(
|
||||||
|
length: 2,
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Audio Gallery'),
|
||||||
|
actions: [
|
||||||
|
if (widget.wolf3d.availableGames.length > 1)
|
||||||
|
GalleryGameSelector(
|
||||||
|
wolf3d: widget.wolf3d,
|
||||||
|
selectedGame: _selectedGame,
|
||||||
|
onSelected: (game) {
|
||||||
|
unawaited(_selectGame(game));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
bottom: const TabBar(
|
||||||
|
tabs: [
|
||||||
|
Tab(icon: Icon(Icons.graphic_eq), text: 'SFX'),
|
||||||
|
Tab(icon: Icon(Icons.music_note), text: 'Music'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: TabBarView(
|
||||||
|
children: [
|
||||||
|
_buildSfxTab(),
|
||||||
|
_buildMusicTab(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSfxTab() {
|
||||||
|
final aliasesById = _buildSfxAliases();
|
||||||
|
final rows = List<_AudioRow>.generate(
|
||||||
|
_selectedGame.sounds.length,
|
||||||
|
(id) => _AudioRow(id: id, aliases: aliasesById[id] ?? const []),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (rows.isEmpty) {
|
||||||
|
return const Center(child: Text('No SFX available in this game data.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
const ListTile(
|
||||||
|
leading: Icon(Icons.info_outline),
|
||||||
|
title: Text('Tap any SFX to play it once'),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
final columns = _gridColumnsForWidth(constraints.maxWidth);
|
||||||
|
return GridView.builder(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: columns,
|
||||||
|
crossAxisSpacing: 8,
|
||||||
|
mainAxisSpacing: 8,
|
||||||
|
childAspectRatio: 2.8,
|
||||||
|
),
|
||||||
|
itemCount: rows.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final row = rows[index];
|
||||||
|
return Card(
|
||||||
|
child: InkWell(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
onTap: () => _playSfx(row.id),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 12,
|
||||||
|
vertical: 10,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
CircleAvatar(child: Text('${row.id}')),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'SFX ${row.id}',
|
||||||
|
style: Theme.of(
|
||||||
|
context,
|
||||||
|
).textTheme.titleSmall,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 2),
|
||||||
|
Text(
|
||||||
|
row.subtitle,
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: Theme.of(
|
||||||
|
context,
|
||||||
|
).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Icon(Icons.play_arrow),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildMusicTab() {
|
||||||
|
final aliasesById = _buildMusicAliases();
|
||||||
|
final rows = List<_AudioRow>.generate(
|
||||||
|
_selectedGame.music.length,
|
||||||
|
(id) => _AudioRow(id: id, aliases: aliasesById[id] ?? const []),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (rows.isEmpty) {
|
||||||
|
return const Center(
|
||||||
|
child: Text('No music tracks available in this game data.'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.info_outline),
|
||||||
|
title: const Text('Tap a track to play, tap again to stop'),
|
||||||
|
subtitle: Text(
|
||||||
|
_playingMusicTrackIndex == null
|
||||||
|
? 'No track currently playing'
|
||||||
|
: 'Playing track ${_playingMusicTrackIndex!}',
|
||||||
|
),
|
||||||
|
trailing: FilledButton.icon(
|
||||||
|
onPressed: _playingMusicTrackIndex == null
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
unawaited(_stopAllAudioPlayback());
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.stop),
|
||||||
|
label: const Text('Stop'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
final columns = _gridColumnsForWidth(constraints.maxWidth);
|
||||||
|
return GridView.builder(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: columns,
|
||||||
|
crossAxisSpacing: 8,
|
||||||
|
mainAxisSpacing: 8,
|
||||||
|
childAspectRatio: 2.8,
|
||||||
|
),
|
||||||
|
itemCount: rows.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final row = rows[index];
|
||||||
|
final bool isPlaying = _playingMusicTrackIndex == row.id;
|
||||||
|
|
||||||
|
return Card(
|
||||||
|
color: isPlaying
|
||||||
|
? Theme.of(context).colorScheme.primaryContainer
|
||||||
|
: null,
|
||||||
|
child: InkWell(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
onTap: () {
|
||||||
|
unawaited(_toggleMusic(row.id));
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 12,
|
||||||
|
vertical: 10,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
CircleAvatar(child: Text('${row.id}')),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Track ${row.id}',
|
||||||
|
style: Theme.of(
|
||||||
|
context,
|
||||||
|
).textTheme.titleSmall,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 2),
|
||||||
|
Text(
|
||||||
|
row.subtitle,
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: Theme.of(
|
||||||
|
context,
|
||||||
|
).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Icon(isPlaying ? Icons.stop : Icons.play_arrow),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ library;
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:wolf_3d_flutter/wolf_3d_flutter.dart';
|
import 'package:wolf_3d_flutter/wolf_3d_flutter.dart';
|
||||||
|
import 'package:wolf_3d_gui/screens/audio_gallery.dart';
|
||||||
import 'package:wolf_3d_gui/screens/sprite_gallery.dart';
|
import 'package:wolf_3d_gui/screens/sprite_gallery.dart';
|
||||||
import 'package:wolf_3d_gui/screens/vga_gallery.dart';
|
import 'package:wolf_3d_gui/screens/vga_gallery.dart';
|
||||||
|
|
||||||
@@ -29,6 +30,21 @@ class DebugToolsScreen extends StatelessWidget {
|
|||||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
|
Card(
|
||||||
|
child: ListTile(
|
||||||
|
leading: const Icon(Icons.library_music),
|
||||||
|
title: const Text('Audio Gallery'),
|
||||||
|
subtitle: const Text('Browse and test SFX and music tracks.'),
|
||||||
|
trailing: const Icon(Icons.chevron_right),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (_) => AudioGallery(wolf3d: wolf3d),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
Card(
|
Card(
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: const Icon(Icons.image_search),
|
leading: const Icon(Icons.image_search),
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ abstract class EngineAudio {
|
|||||||
void playMenuMusic();
|
void playMenuMusic();
|
||||||
void playLevelMusic(WolfLevel level);
|
void playLevelMusic(WolfLevel level);
|
||||||
void stopMusic();
|
void stopMusic();
|
||||||
|
Future<void> stopAllAudio();
|
||||||
void playSoundEffect(int sfxId);
|
void playSoundEffect(int sfxId);
|
||||||
Future<void> init();
|
Future<void> init();
|
||||||
void dispose();
|
void dispose();
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ class CliSilentAudio implements EngineAudio {
|
|||||||
@override
|
@override
|
||||||
void stopMusic() {}
|
void stopMusic() {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> stopAllAudio() async {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void playSoundEffect(int sfxId) {
|
void playSoundEffect(int sfxId) {
|
||||||
// Optional: You could use the terminal 'bell' character here
|
// Optional: You could use the terminal 'bell' character here
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ class WolfAudio implements EngineAudio {
|
|||||||
/// Disposes of the audio engine and frees resources.
|
/// Disposes of the audio engine and frees resources.
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
stopMusic();
|
stopAllAudio();
|
||||||
_musicPlayer.dispose();
|
_musicPlayer.dispose();
|
||||||
|
|
||||||
for (final player in _sfxPlayers) {
|
for (final player in _sfxPlayers) {
|
||||||
@@ -101,6 +101,16 @@ class WolfAudio implements EngineAudio {
|
|||||||
await _musicPlayer.stop();
|
await _musicPlayer.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> stopAllAudio() async {
|
||||||
|
if (!_isInitialized) return;
|
||||||
|
|
||||||
|
await _musicPlayer.stop();
|
||||||
|
for (final player in _sfxPlayers) {
|
||||||
|
await player.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> pauseMusic() async {
|
Future<void> pauseMusic() async {
|
||||||
if (_isInitialized) await _musicPlayer.pause();
|
if (_isInitialized) await _musicPlayer.pause();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -181,6 +181,9 @@ class _CapturingAudio implements EngineAudio {
|
|||||||
@override
|
@override
|
||||||
void stopMusic() {}
|
void stopMusic() {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> stopAllAudio() async {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {}
|
void dispose() {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,6 +130,9 @@ class _SilentAudio implements EngineAudio {
|
|||||||
@override
|
@override
|
||||||
void stopMusic() {}
|
void stopMusic() {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> stopAllAudio() async {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {}
|
void dispose() {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -407,6 +407,9 @@ class _SilentAudio implements EngineAudio {
|
|||||||
@override
|
@override
|
||||||
void stopMusic() {}
|
void stopMusic() {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> stopAllAudio() async {}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {}
|
void dispose() {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,11 @@ class FlutterAudioAdapter implements EngineAudio {
|
|||||||
wolf3d.audio.stopMusic();
|
wolf3d.audio.stopMusic();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> stopAllAudio() async {
|
||||||
|
await wolf3d.audio.stopAllAudio();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void playSoundEffect(int sfxId) {
|
void playSoundEffect(int sfxId) {
|
||||||
wolf3d.audio.playSoundEffect(sfxId);
|
wolf3d.audio.playSoundEffect(sfxId);
|
||||||
|
|||||||
Reference in New Issue
Block a user