Initial commit: dynamic music & FX players
Continuous music players (DynamicSoundPlayer, 2D, 3D) with intensity-blended layers, playlist queue, Loop, fade in/out, pause/resume. One-shot SFX players (DynamicSoundFXPlayer, 2D, 3D) for overlapping triggers. All variants share RefCounted core classes via duck-typed property access. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
@tool
|
||||
@icon("res://addons/gd-dynamic-sound/icon.svg")
|
||||
class_name DynamicSoundPlayer2D
|
||||
## 2D positional variant of [DynamicSoundPlayer].
|
||||
##
|
||||
## Same playlist, intensity, fade, and pause behaviour — distance-attenuated
|
||||
## from a 2D position relative to the listener.
|
||||
extends AudioStreamPlayer2D
|
||||
|
||||
|
||||
## Songs to play in order. Mutated as songs finish: each finished song is
|
||||
## popped, and re-appended at the back when [member Loop] is on.
|
||||
@export var Playlist : DynamicSoundPlaylist;
|
||||
|
||||
## Runtime mix between the three intensity layers. [code]0[/code] = least intense, [code]1[/code] = most intense.
|
||||
@export_range(0, 1) var Intensity : float = 0:
|
||||
set(value):
|
||||
Intensity = value;
|
||||
if _core: _core.apply_stream_volumes(volume_db);
|
||||
|
||||
## When true, finished songs are re-appended to the back of the queue so the playlist cycles forever.
|
||||
@export var Loop : bool;
|
||||
|
||||
## How playback pitch is selected.
|
||||
@export var PitchMode : DynamicSoundConstants.PitchMode = DynamicSoundConstants.PitchMode.Constant:
|
||||
set(value):
|
||||
PitchMode = value;
|
||||
notify_property_list_changed();
|
||||
|
||||
## Minimum pitch scale when [member PitchMode] is Random.
|
||||
@export var RandomMinPitchScale : float = -0.5;
|
||||
## Maximum pitch scale when [member PitchMode] is Random.
|
||||
@export var RandomMaxPitchScale : float = 0.5;
|
||||
|
||||
var _core : DynamicSoundPlayerCore;
|
||||
|
||||
func _set(property: StringName, value: Variant) -> bool:
|
||||
if property == &"volume_db":
|
||||
if _core: _core.apply_stream_volumes(value);
|
||||
return false;
|
||||
return false;
|
||||
|
||||
func _enter_tree() -> void:
|
||||
if Engine.is_editor_hint() and stream == null:
|
||||
stream = AudioStreamPolyphonic.new();
|
||||
|
||||
func _validate_property(property: Dictionary) -> void:
|
||||
if property.name in ["RandomMinPitchScale", "RandomMaxPitchScale"]:
|
||||
if PitchMode != DynamicSoundConstants.PitchMode.Random:
|
||||
property.usage &= ~PROPERTY_USAGE_EDITOR;
|
||||
|
||||
func _ready() -> void:
|
||||
_core = DynamicSoundPlayerCore.new(self);
|
||||
_core.ready();
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if _core: _core.process(delta);
|
||||
|
||||
## Pause playback. Call [method resume] to continue.
|
||||
func pause() -> void:
|
||||
if _core: _core.pause();
|
||||
|
||||
## Resume playback after a [method pause].
|
||||
func resume() -> void:
|
||||
if _core: _core.resume();
|
||||
|
||||
## True if a song is currently playing (and not paused).
|
||||
func is_song_playing() -> bool:
|
||||
return _core != null and _core.is_song_playing();
|
||||
|
||||
## Start playback (if needed) and fade volume up from silent over [param duration] seconds.
|
||||
func fade_in(duration: float) -> void:
|
||||
if _core: _core.fade_in(duration);
|
||||
|
||||
## Fade volume to silent over [param duration] seconds, then stop playback.
|
||||
func fade_out(duration: float) -> void:
|
||||
if _core: _core.fade_out(duration);
|
||||
Reference in New Issue
Block a user