Play (wrong) door sound effect when the door opens or closes
Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
@@ -2,9 +2,10 @@ import 'dart:typed_data';
|
||||
|
||||
import 'package:audioplayers/audioplayers.dart';
|
||||
import 'package:wolf_3d_data_types/wolf_3d_data_types.dart';
|
||||
import 'package:wolf_3d_engine/wolf_3d_engine.dart';
|
||||
import 'package:wolf_3d_synth/src/imf_renderer.dart';
|
||||
|
||||
class WolfAudio {
|
||||
class WolfAudio implements EngineAudio {
|
||||
bool _isInitialized = false;
|
||||
|
||||
// --- Music State ---
|
||||
@@ -16,9 +17,11 @@ class WolfAudio {
|
||||
final List<AudioPlayer> _sfxPlayers = [];
|
||||
int _currentSfxIndex = 0;
|
||||
|
||||
@override
|
||||
WolfensteinData? activeGame;
|
||||
|
||||
/// Initializes the audio engine and pre-allocates the SFX pool.
|
||||
@override
|
||||
Future<void> init() async {
|
||||
if (_isInitialized) return;
|
||||
|
||||
@@ -45,6 +48,7 @@ class WolfAudio {
|
||||
}
|
||||
|
||||
/// Disposes of the audio engine and frees resources.
|
||||
@override
|
||||
void dispose() {
|
||||
stopMusic();
|
||||
_musicPlayer.dispose();
|
||||
@@ -79,6 +83,7 @@ class WolfAudio {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> stopMusic() async {
|
||||
if (!_isInitialized) return;
|
||||
await _musicPlayer.stop();
|
||||
@@ -92,12 +97,14 @@ class WolfAudio {
|
||||
if (_isInitialized) await _musicPlayer.resume();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> playMenuMusic() async {
|
||||
final data = activeGame;
|
||||
if (data == null || data.music.length <= 1) return;
|
||||
await playMusic(data.music[1]);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> playLevelMusic(WolfLevel level) async {
|
||||
final data = activeGame;
|
||||
if (data == null || data.music.isEmpty) return;
|
||||
@@ -114,21 +121,38 @@ class WolfAudio {
|
||||
// SFX MANAGEMENT
|
||||
// ==========================================
|
||||
|
||||
/// Plays a sound effect from a WAV byte array using the round-robin pool.
|
||||
Future<void> playSfx(Uint8List wavBytes) async {
|
||||
if (!_isInitialized) return;
|
||||
@override
|
||||
Future<void> playSoundEffect(int sfxId) async {
|
||||
print("Playing sfx id $sfxId");
|
||||
if (!_isInitialized || activeGame == null) return;
|
||||
|
||||
// 1. Get the raw 8-bit unsigned PCM bytes from the game data
|
||||
final Uint8List raw8bitBytes = activeGame!.sounds[sfxId].bytes;
|
||||
if (raw8bitBytes.isEmpty) return;
|
||||
|
||||
// 2. Convert 8-bit Unsigned PCM -> 16-bit Signed PCM
|
||||
// Wolf3D raw sounds are biased at 128.
|
||||
final Int16List converted16bit = Int16List(raw8bitBytes.length);
|
||||
for (int i = 0; i < raw8bitBytes.length; i++) {
|
||||
// (sample - 128) shifts it to signed 8-bit range (-128 to 127)
|
||||
// Multiplying by 256 scales it to 16-bit range (-32768 to 32512)
|
||||
converted16bit[i] = (raw8bitBytes[i] - 128) * 256;
|
||||
}
|
||||
|
||||
// 3. Wrap in a WAV header (Wolf3D digitized sounds are 7000Hz Mono)
|
||||
final wavBytes = ImfRenderer.createWavFile(
|
||||
converted16bit,
|
||||
sampleRate: 7000,
|
||||
);
|
||||
|
||||
try {
|
||||
// Grab the next available player in the pool
|
||||
final player = _sfxPlayers[_currentSfxIndex];
|
||||
|
||||
// Move to the next index, looping back to 0 if we hit the max
|
||||
_currentSfxIndex = (_currentSfxIndex + 1) % _maxSfxChannels;
|
||||
|
||||
// Play the sound (this interrupts whatever this specific channel was playing)
|
||||
// Note: We use BytesSource because createWavFile returns Uint8List (the file bytes)
|
||||
await player.play(BytesSource(wavBytes));
|
||||
} catch (e) {
|
||||
print("WolfAudio: Error playing SFX - $e");
|
||||
print("WolfAudio: SFX Error - $e");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user