Files
wolf_dart/packages/wolf_3d_flutter/lib/audio/wolf_audio.dart
T

185 lines
4.6 KiB
Dart

import 'dart:developer';
import 'dart:typed_data';
import 'package:audioplayers/audioplayers.dart';
import 'package:wolf_3d_dart/wolf_3d_data_types.dart';
import 'package:wolf_3d_dart/wolf_3d_engine.dart';
import 'package:wolf_3d_dart/wolf_3d_synth.dart';
import 'package:wolf_3d_flutter/audio/debug_music_player.dart';
class WolfAudio implements EngineAudio, DebugMusicPlayer {
@override
Future<void> debugSoundTest() async {
for (int i = 0; i < 50; i++) {
Future.delayed(Duration(seconds: i * 2), () {
log('[AUDIO] Testing Sound ID: $i');
playSoundEffectId(i);
});
}
}
bool _isInitialized = false;
final AudioPlayer _musicPlayer = AudioPlayer();
static const int _maxSfxChannels = 8;
final List<AudioPlayer> _sfxPlayers = [];
int _currentSfxIndex = 0;
@override
WolfensteinData? activeGame;
@override
Future<void> init() async {
if (_isInitialized) return;
try {
await _musicPlayer.setPlayerMode(PlayerMode.mediaPlayer);
for (int i = 0; i < _maxSfxChannels; i++) {
final player = AudioPlayer();
await player.setPlayerMode(PlayerMode.lowLatency);
await player.setReleaseMode(ReleaseMode.stop);
_sfxPlayers.add(player);
}
_isInitialized = true;
log(
'[AUDIO] AudioPlayers initialized successfully with $_maxSfxChannels SFX channels.',
);
} catch (e) {
log('[AUDIO] Failed to initialize AudioPlayers - $e');
}
}
@override
void dispose() {
stopAllAudio();
_musicPlayer.dispose();
for (final player in _sfxPlayers) {
player.stop();
player.dispose();
}
_sfxPlayers.clear();
_isInitialized = false;
}
@override
Future<void> playMusic(ImfMusic track, {bool looping = true}) async {
if (!_isInitialized) return;
await stopMusic();
try {
final pcmSamples = ImfRenderer.render(track);
final wavBytes = ImfRenderer.createWavFile(pcmSamples);
await _musicPlayer.setReleaseMode(
looping ? ReleaseMode.loop : ReleaseMode.stop,
);
await _musicPlayer.play(BytesSource(wavBytes));
} catch (e) {
log('[AUDIO] Error playing music track - $e');
}
}
@override
Future<void> stopMusic() async {
if (!_isInitialized) return;
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 {
if (_isInitialized) await _musicPlayer.pause();
}
Future<void> resumeMusic() async {
if (_isInitialized) await _musicPlayer.resume();
}
@override
Future<void> playMenuMusic() async {
final data = activeGame;
final trackIndex = data == null
? null
: Music.menuTheme.trackIndexFor(data.version);
if (data == null || trackIndex == null || trackIndex >= data.music.length) {
return;
}
await playMusic(data.music[trackIndex]);
}
@override
Future<void> playLevelMusic(Music music) async {
final data = activeGame;
if (data == null || data.music.isEmpty) return;
final index = music.trackIndexFor(data.version) ?? 0;
if (index < data.music.length) {
await playMusic(data.music[index]);
} else {
log('[AUDIO] Warning - Track index $index out of bounds.');
}
}
@override
Future<void> playSoundEffect(SoundEffect effect) async {
final data = activeGame;
if (data == null) return;
final resolved = data.registry.sfx.resolve(effect);
if (resolved != null) {
await playSoundEffectId(resolved.slotIndex);
return;
}
if (data.version == GameVersion.spearOfDestinyDemo) {
return;
}
await playSoundEffectId(effect.idFor(data.version));
}
@override
Future<void> playSoundEffectId(int sfxId) async {
log('[AUDIO] Playing sfx id $sfxId');
final data = activeGame;
if (data == null) return;
final soundsList = data.sounds;
if (sfxId < 0 || sfxId >= soundsList.length) return;
final raw8bitBytes = soundsList[sfxId].bytes;
if (raw8bitBytes.isEmpty) return;
final Int16List converted16bit = Int16List(raw8bitBytes.length);
for (int i = 0; i < raw8bitBytes.length; i++) {
converted16bit[i] = (raw8bitBytes[i] - 128) * 256;
}
final wavBytes = ImfRenderer.createWavFile(
converted16bit,
sampleRate: 7000,
);
try {
final player = _sfxPlayers[_currentSfxIndex];
_currentSfxIndex = (_currentSfxIndex + 1) % _maxSfxChannels;
await player.play(BytesSource(wavBytes));
} catch (e) {
log('[AUDIO] SFX Error - $e');
}
}
}