97 lines
3.0 KiB
GDScript
97 lines
3.0 KiB
GDScript
extends Node
|
|
## Separate effects from ambience so pellet sounds never restart the siren.
|
|
## Tune this to the end of the fourth wah in death_0.wav.
|
|
const DEATH_INTRO_SECONDS := 1.35
|
|
var death_cutoff: Timer
|
|
var effects: Array[AudioStreamPlayer] = []
|
|
var ambience: AudioStreamPlayer
|
|
var streams := {}
|
|
var background := ""
|
|
var effect_cursor := 0
|
|
var death_tail := false
|
|
var intro_pending := false
|
|
|
|
func _ready() -> void:
|
|
death_cutoff = Timer.new()
|
|
death_cutoff.one_shot = true
|
|
add_child(death_cutoff)
|
|
death_cutoff.timeout.connect(_death_finished)
|
|
for i in range(4):
|
|
var voice := AudioStreamPlayer.new()
|
|
voice.volume_db = -10.0
|
|
add_child(voice)
|
|
effects.append(voice)
|
|
ambience = AudioStreamPlayer.new()
|
|
ambience.volume_db = -15.0
|
|
add_child(ambience)
|
|
ambience.finished.connect(_ambient_finished)
|
|
effects[3].finished.connect(_death_finished)
|
|
|
|
func stream(name: String) -> AudioStream:
|
|
if not streams.has(name):
|
|
var path := "res://games/pacman/audio/%s.wav" % name
|
|
if not ResourceLoader.exists(path): return null
|
|
streams[name] = load(path)
|
|
return streams[name]
|
|
|
|
func play_effect(name: String) -> void:
|
|
if name == "death":
|
|
stop_all()
|
|
death_tail = true
|
|
effects[3].stream = stream("death_0")
|
|
effects[3].play()
|
|
death_cutoff.start(DEATH_INTRO_SECONDS)
|
|
return
|
|
var voice: AudioStreamPlayer = effects[0] if name.begins_with("eat_dot") else effects[1 + effect_cursor % 2]
|
|
if not name.begins_with("eat_dot"): effect_cursor += 1
|
|
voice.stream = stream(name)
|
|
if voice.stream != null: voice.play()
|
|
|
|
func _death_finished() -> void:
|
|
if not death_tail: return
|
|
death_tail = false
|
|
death_cutoff.stop()
|
|
effects[3].stop()
|
|
effects[3].stream = stream("death_1")
|
|
effects[3].play()
|
|
|
|
func set_background(name: String) -> void:
|
|
if name == background: return
|
|
background = name
|
|
ambience.stop()
|
|
intro_pending = false
|
|
if name.is_empty(): return
|
|
var intro := stream(name + "_firstloop")
|
|
# Join PCM samples before playback: no finished-signal gap at the intro seam.
|
|
ambience.stream = _loop_stream(name, intro)
|
|
if ambience.stream != null: ambience.play()
|
|
|
|
func _loop_stream(name: String, intro: AudioStream = null) -> AudioStream:
|
|
var source := stream(name)
|
|
if source is AudioStreamWAV:
|
|
var loop: AudioStreamWAV = source.duplicate()
|
|
loop.loop_mode = AudioStreamWAV.LOOP_FORWARD
|
|
loop.loop_begin = 0
|
|
loop.loop_end = roundi(loop.get_length() * loop.mix_rate)
|
|
if intro is AudioStreamWAV and intro.format == loop.format and intro.mix_rate == loop.mix_rate and intro.stereo == loop.stereo:
|
|
var intro_frames := roundi(intro.get_length() * intro.mix_rate)
|
|
loop.data = intro.data + loop.data
|
|
loop.loop_begin = intro_frames
|
|
loop.loop_end += intro_frames
|
|
return loop
|
|
return source
|
|
|
|
func _ambient_finished() -> void:
|
|
if background.is_empty(): return
|
|
intro_pending = false
|
|
ambience.stream = _loop_stream(background)
|
|
if ambience.stream != null: ambience.play()
|
|
|
|
func stop_all() -> void:
|
|
background = ""
|
|
death_tail = false
|
|
intro_pending = false
|
|
if death_cutoff != null: death_cutoff.stop()
|
|
if ambience != null: ambience.stop()
|
|
for voice in effects: voice.stop()
|