diff --git a/doc/classes/AudioServer.xml b/doc/classes/AudioServer.xml
index 16437fd7d863..addb3070413d 100644
--- a/doc/classes/AudioServer.xml
+++ b/doc/classes/AudioServer.xml
@@ -35,6 +35,14 @@
Generates an [AudioBusLayout] using the available buses and effects.
+
+
+
+ Returns the absolute time in seconds of the [AudioServer]'s timeline, based on the number of audio frames mixed. Used to schedule sounds to be played with high precision timing, such as with [method ScheduledAudioStreamPlayer.play_scheduled].
+ See also [AudioStreamPlaybackScheduled].
+ [b]Note:[/b] This value only updates each time an audio chunk is mixed and should not be relied on as an accurate "current" time of the [AudioServer].
+
+
diff --git a/doc/classes/AudioStreamPlaybackScheduled.xml b/doc/classes/AudioStreamPlaybackScheduled.xml
new file mode 100644
index 000000000000..3bf0d8ca18d8
--- /dev/null
+++ b/doc/classes/AudioStreamPlaybackScheduled.xml
@@ -0,0 +1,60 @@
+
+
+
+ Wrapper around [AudioStreamPlayback] that schedules it to be played in the future.
+
+
+ Wrapper around [AudioStreamPlayback] that schedules it to be played in the future.
+ An [AudioStreamPlaybackScheduled] instance is created from [method ScheduledAudioStreamPlayer.play_scheduled], [method ScheduledAudioStreamPlayer2D.play_scheduled], or [method ScheduledAudioStreamPlayer3D.play_scheduled].
+ [codeblocks]
+ [gdscript]
+ var audio_time = AudioServer.get_absolute_time()
+
+ # Play the sound roughly 1 second in the future, then stop it after exactly 3 seconds.
+ var playback_scheduled = player.play_scheduled(audio_time + 1)
+ playback_scheduled.scheduled_end_time = audio_time + 4
+
+ # Cancel the playback
+ playback_scheduled.cancel()
+ [/gdscript]
+ [csharp]
+ double audioTime = AudioServer.GetAbsoluteTime();
+
+ // Play the sound roughly 1 second in the future, then stop it after exactly 3 seconds.
+ AudioStreamPlaybackScheduled playbackScheduled = player.PlayScheduled(audioTime + 1);
+ playbackScheduled.ScheduledEndTime = audioTime + 4;
+
+ // Cancel the playback
+ playbackScheduled.Cancel();
+ [/csharp]
+ [/codeblocks]
+
+
+
+
+
+
+
+ Cancels the currently scheduled playback.
+ If the playback is actively playing, this method does nothing. Use [method AudioStreamPlayback.stop] instead to both stop and cancel the scheduled playback.
+
+
+
+
+
+ Returns [code]true[/code] if the playback is scheduled and has not yet started.
+
+
+
+
+
+ The wrapped [AudioStreamPlayback] to be scheduled. If set, the previously scheduled playback will be cancelled/stopped.
+
+
+ The time the playback is scheduled to stop playing in seconds. This is based on the [AudioServer]'s timeline (see [method AudioServer.get_absolute_time]).
+
+
+ The time the playback is scheduled to start playing in seconds. This is based on the [AudioServer]'s timeline (see [method AudioServer.get_absolute_time]).
+
+
+
diff --git a/doc/classes/ScheduledAudioStreamPlayer.xml b/doc/classes/ScheduledAudioStreamPlayer.xml
new file mode 100644
index 000000000000..d13d649d045f
--- /dev/null
+++ b/doc/classes/ScheduledAudioStreamPlayer.xml
@@ -0,0 +1,26 @@
+
+
+
+ A node for scheduled audio playback.
+
+
+ Behaves similarly to [AudioStreamPlayer] but can schedule audio to play in the future.
+ If you need to play scheduled audio at a specific position, use [ScheduledAudioStreamPlayer2D] or [ScheduledAudioStreamPlayer3D] instead.
+
+
+
+
+
+
+
+
+
+ Schedules a sound to be played on the [AudioServer]'s timeline at [param absolute_time] in seconds. If the sound is scheduled to play earlier than the value returned by [method AudioServer.get_absolute_time], it will be played immediately. The sound starts from the given [param from_position] in seconds.
+ Use this method for high precision playbacks, such as a metronome or other rhythm-based sounds.
+ Returns an [AudioStreamPlaybackScheduled] instance representing the scheduled playback of the sound.
+ [b]Note:[/b] Calling this method after [member AudioStreamPlayer.max_polyphony] is reached will cut off the oldest sound playing on this node.
+ [b]Note:[/b] On the Web platform, [member AudioStreamPlayer.playback_type] must be set to [constant AudioServer.PLAYBACK_TYPE_STREAM]. Otherwise, this method will behave like [method AudioStreamPlayer.play].
+
+
+
+
diff --git a/doc/classes/ScheduledAudioStreamPlayer2D.xml b/doc/classes/ScheduledAudioStreamPlayer2D.xml
new file mode 100644
index 000000000000..72691f053c6a
--- /dev/null
+++ b/doc/classes/ScheduledAudioStreamPlayer2D.xml
@@ -0,0 +1,26 @@
+
+
+
+ A node for scheduled audio playback.
+
+
+ Behaves similarly to [AudioStreamPlayer2D] but can schedule audio to play in the future.
+ See also [ScheduledAudioStreamPlayer] to play a scheduled sound non-positionally.
+
+
+
+
+
+
+
+
+
+ Schedules a sound to be played on the [AudioServer]'s timeline at [param absolute_time] in seconds. If the sound is scheduled to play earlier than the value returned by [method AudioServer.get_absolute_time], it will be played immediately. The sound starts from the given [param from_position] in seconds.
+ Use this method for high precision playbacks, such as a metronome or other rhythm-based sounds.
+ Returns an [AudioStreamPlaybackScheduled] instance representing the scheduled playback of the sound.
+ [b]Note:[/b] Calling this method after [member AudioStreamPlayer2D.max_polyphony] is reached will cut off the oldest sound playing on this node.
+ [b]Note:[/b] On the Web platform, [member AudioStreamPlayer2D.playback_type] must be set to [constant AudioServer.PLAYBACK_TYPE_STREAM]. Otherwise, this method will behave like [method AudioStreamPlayer2D.play].
+
+
+
+
diff --git a/doc/classes/ScheduledAudioStreamPlayer3D.xml b/doc/classes/ScheduledAudioStreamPlayer3D.xml
new file mode 100644
index 000000000000..09c676940176
--- /dev/null
+++ b/doc/classes/ScheduledAudioStreamPlayer3D.xml
@@ -0,0 +1,26 @@
+
+
+
+ A node for scheduled audio playback.
+
+
+ Behaves similarly to [AudioStreamPlayer3D] but can schedule audio to play in the future.
+ See also [ScheduledAudioStreamPlayer] to play a scheduled sound non-positionally.
+
+
+
+
+
+
+
+
+
+ Schedules a sound to be played on the [AudioServer]'s timeline at [param absolute_time] in seconds. If the sound is scheduled to play earlier than the value returned by [method AudioServer.get_absolute_time], it will be played immediately. The sound starts from the given [param from_position] in seconds.
+ Use this method for high precision playbacks, such as a metronome or other rhythm-based sounds.
+ Returns an [AudioStreamPlaybackScheduled] instance representing the scheduled playback of the sound.
+ [b]Note:[/b] Calling this method after [member AudioStreamPlayer3D.max_polyphony] is reached will cut off the oldest sound playing on this node.
+ [b]Note:[/b] On the Web platform, [member AudioStreamPlayer3D.playback_type] must be set to [constant AudioServer.PLAYBACK_TYPE_STREAM]. Otherwise, this method will behave like [method AudioStreamPlayer3D.play].
+
+
+
+
diff --git a/editor/icons/ScheduledAudioStreamPlayer.svg b/editor/icons/ScheduledAudioStreamPlayer.svg
new file mode 100644
index 000000000000..064193640577
--- /dev/null
+++ b/editor/icons/ScheduledAudioStreamPlayer.svg
@@ -0,0 +1 @@
+
diff --git a/editor/icons/ScheduledAudioStreamPlayer2D.svg b/editor/icons/ScheduledAudioStreamPlayer2D.svg
new file mode 100644
index 000000000000..3e7684d2674e
--- /dev/null
+++ b/editor/icons/ScheduledAudioStreamPlayer2D.svg
@@ -0,0 +1 @@
+
diff --git a/editor/icons/ScheduledAudioStreamPlayer3D.svg b/editor/icons/ScheduledAudioStreamPlayer3D.svg
new file mode 100644
index 000000000000..6fe884c991f0
--- /dev/null
+++ b/editor/icons/ScheduledAudioStreamPlayer3D.svg
@@ -0,0 +1 @@
+
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index e1fa6d5547f7..09b4c459c49c 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -231,11 +231,7 @@ float AudioStreamPlayer2D::get_pitch_scale() const {
return internal->pitch_scale;
}
-void AudioStreamPlayer2D::play(float p_from_pos) {
- Ref stream_playback = internal->play_basic();
- if (stream_playback.is_null()) {
- return;
- }
+void AudioStreamPlayer2D::_play_internal(Ref stream_playback, double p_from_pos) {
setplayback = stream_playback;
setplay.set(p_from_pos);
@@ -249,6 +245,14 @@ void AudioStreamPlayer2D::play(float p_from_pos) {
}
}
+void AudioStreamPlayer2D::play(float p_from_pos) {
+ Ref stream_playback = internal->play_basic();
+ if (stream_playback.is_null()) {
+ return;
+ }
+ _play_internal(stream_playback, p_from_pos);
+}
+
void AudioStreamPlayer2D::seek(float p_seconds) {
internal->seek(p_seconds);
}
diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h
index b3e996922a91..e30c333c9af1 100644
--- a/scene/2d/audio_stream_player_2d.h
+++ b/scene/2d/audio_stream_player_2d.h
@@ -41,6 +41,9 @@ class AudioStreamPlayerInternal;
class AudioStreamPlayer2D : public Node2D {
GDCLASS(AudioStreamPlayer2D, Node2D);
+protected:
+ AudioStreamPlayerInternal *internal = nullptr;
+
private:
enum {
MAX_OUTPUTS = 8,
@@ -48,8 +51,6 @@ class AudioStreamPlayer2D : public Node2D {
};
- AudioStreamPlayerInternal *internal = nullptr;
-
SafeNumeric setplay{ -1.0 };
Ref setplayback;
@@ -85,6 +86,8 @@ class AudioStreamPlayer2D : public Node2D {
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List *p_list) const;
+ void _play_internal(Ref stream_playback, double p_from_pos = 0.0);
+
#ifndef DISABLE_DEPRECATED
bool _is_autoplay_enabled_bind_compat_86907();
static void _bind_compatibility_methods();
diff --git a/scene/2d/scheduled_audio_stream_player_2d.cpp b/scene/2d/scheduled_audio_stream_player_2d.cpp
new file mode 100644
index 000000000000..0de9dd4ea32c
--- /dev/null
+++ b/scene/2d/scheduled_audio_stream_player_2d.cpp
@@ -0,0 +1,49 @@
+/**************************************************************************/
+/* scheduled_audio_stream_player_2d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "scheduled_audio_stream_player_2d.h"
+
+#include "scene/audio/audio_stream_player_internal.h"
+#include "scene/resources/audio_stream_playback_scheduled.h"
+
+Ref ScheduledAudioStreamPlayer2D::play_scheduled(double p_abs_time, double p_from_pos) {
+ Ref stream_playback_scheduled = internal->play_scheduled_basic();
+ if (stream_playback_scheduled.is_null()) {
+ return stream_playback_scheduled;
+ }
+ stream_playback_scheduled->set_scheduled_start_time(p_abs_time);
+ _play_internal(stream_playback_scheduled, p_from_pos);
+
+ return stream_playback_scheduled;
+}
+
+void ScheduledAudioStreamPlayer2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("play_scheduled", "absolute_time", "from_position"), &ScheduledAudioStreamPlayer2D::play_scheduled, DEFVAL(0.0));
+}
diff --git a/scene/2d/scheduled_audio_stream_player_2d.h b/scene/2d/scheduled_audio_stream_player_2d.h
new file mode 100644
index 000000000000..d91ac769c61c
--- /dev/null
+++ b/scene/2d/scheduled_audio_stream_player_2d.h
@@ -0,0 +1,45 @@
+/**************************************************************************/
+/* scheduled_audio_stream_player_2d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#pragma once
+
+#include "audio_stream_player_2d.h"
+
+class AudioStreamPlaybackScheduled;
+
+class ScheduledAudioStreamPlayer2D : public AudioStreamPlayer2D {
+ GDCLASS(ScheduledAudioStreamPlayer2D, AudioStreamPlayer2D);
+
+protected:
+ static void _bind_methods();
+
+public:
+ Ref play_scheduled(double p_abs_time, double p_from_pos = 0.0);
+};
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index ea05c6bbbd80..dbea0d78eaf9 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -591,11 +591,7 @@ float AudioStreamPlayer3D::get_pitch_scale() const {
return internal->pitch_scale;
}
-void AudioStreamPlayer3D::play(float p_from_pos) {
- Ref stream_playback = internal->play_basic();
- if (stream_playback.is_null()) {
- return;
- }
+void AudioStreamPlayer3D::_play_internal(Ref stream_playback, double p_from_pos) {
setplayback = stream_playback;
setplay.set(p_from_pos);
@@ -609,6 +605,14 @@ void AudioStreamPlayer3D::play(float p_from_pos) {
}
}
+void AudioStreamPlayer3D::play(float p_from_pos) {
+ Ref stream_playback = internal->play_basic();
+ if (stream_playback.is_null()) {
+ return;
+ }
+ _play_internal(stream_playback, p_from_pos);
+}
+
void AudioStreamPlayer3D::seek(float p_seconds) {
internal->seek(p_seconds);
}
diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h
index 69d41c44fcd6..c82492021f31 100644
--- a/scene/3d/audio_stream_player_3d.h
+++ b/scene/3d/audio_stream_player_3d.h
@@ -59,6 +59,9 @@ class AudioStreamPlayer3D : public Node3D {
DOPPLER_TRACKING_PHYSICS_STEP
};
+protected:
+ AudioStreamPlayerInternal *internal = nullptr;
+
private:
enum {
MAX_OUTPUTS = 8,
@@ -66,8 +69,6 @@ class AudioStreamPlayer3D : public Node3D {
};
- AudioStreamPlayerInternal *internal = nullptr;
-
SafeNumeric setplay{ -1.0 };
Ref setplayback;
@@ -130,6 +131,8 @@ class AudioStreamPlayer3D : public Node3D {
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List *p_list) const;
+ void _play_internal(Ref stream_playback, double p_from_pos = 0.0);
+
#ifndef DISABLE_DEPRECATED
bool _is_autoplay_enabled_bind_compat_86907();
static void _bind_compatibility_methods();
diff --git a/scene/3d/scheduled_audio_stream_player_3d.cpp b/scene/3d/scheduled_audio_stream_player_3d.cpp
new file mode 100644
index 000000000000..f3b781193e9b
--- /dev/null
+++ b/scene/3d/scheduled_audio_stream_player_3d.cpp
@@ -0,0 +1,49 @@
+/**************************************************************************/
+/* scheduled_audio_stream_player_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "scheduled_audio_stream_player_3d.h"
+
+#include "scene/audio/audio_stream_player_internal.h"
+#include "scene/resources/audio_stream_playback_scheduled.h"
+
+Ref ScheduledAudioStreamPlayer3D::play_scheduled(double p_abs_time, double p_from_pos) {
+ Ref stream_playback_scheduled = internal->play_scheduled_basic();
+ if (stream_playback_scheduled.is_null()) {
+ return stream_playback_scheduled;
+ }
+ stream_playback_scheduled->set_scheduled_start_time(p_abs_time);
+ _play_internal(stream_playback_scheduled, p_from_pos);
+
+ return stream_playback_scheduled;
+}
+
+void ScheduledAudioStreamPlayer3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("play_scheduled", "absolute_time", "from_position"), &ScheduledAudioStreamPlayer3D::play_scheduled, DEFVAL(0.0));
+}
diff --git a/scene/3d/scheduled_audio_stream_player_3d.h b/scene/3d/scheduled_audio_stream_player_3d.h
new file mode 100644
index 000000000000..21d2989ecba0
--- /dev/null
+++ b/scene/3d/scheduled_audio_stream_player_3d.h
@@ -0,0 +1,45 @@
+/**************************************************************************/
+/* scheduled_audio_stream_player_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#pragma once
+
+#include "audio_stream_player_3d.h"
+
+class AudioStreamPlaybackScheduled;
+
+class ScheduledAudioStreamPlayer3D : public AudioStreamPlayer3D {
+ GDCLASS(ScheduledAudioStreamPlayer3D, AudioStreamPlayer3D);
+
+protected:
+ static void _bind_methods();
+
+public:
+ Ref play_scheduled(double p_abs_time, double p_from_pos = 0.0);
+};
diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp
index 531b3c0d50a5..471b2e4712ec 100644
--- a/scene/audio/audio_stream_player.cpp
+++ b/scene/audio/audio_stream_player.cpp
@@ -104,11 +104,7 @@ int AudioStreamPlayer::get_max_polyphony() const {
return internal->max_polyphony;
}
-void AudioStreamPlayer::play(float p_from_pos) {
- Ref stream_playback = internal->play_basic();
- if (stream_playback.is_null()) {
- return;
- }
+void AudioStreamPlayer::_play_internal(Ref stream_playback, double p_from_pos) {
AudioServer::get_singleton()->start_playback_stream(stream_playback, internal->bus, _get_volume_vector(), p_from_pos, internal->pitch_scale);
internal->ensure_playback_limit();
@@ -123,6 +119,14 @@ void AudioStreamPlayer::play(float p_from_pos) {
}
}
+void AudioStreamPlayer::play(float p_from_pos) {
+ Ref stream_playback = internal->play_basic();
+ if (stream_playback.is_null()) {
+ return;
+ }
+ _play_internal(stream_playback, p_from_pos);
+}
+
void AudioStreamPlayer::seek(float p_seconds) {
internal->seek(p_seconds);
}
diff --git a/scene/audio/audio_stream_player.h b/scene/audio/audio_stream_player.h
index abaed688d1e4..db6416b963cc 100644
--- a/scene/audio/audio_stream_player.h
+++ b/scene/audio/audio_stream_player.h
@@ -48,9 +48,10 @@ class AudioStreamPlayer : public Node {
MIX_TARGET_CENTER
};
-private:
+protected:
AudioStreamPlayerInternal *internal = nullptr;
+private:
MixTarget mix_target = MIX_TARGET_STEREO;
void _set_playing(bool p_enable);
@@ -67,6 +68,8 @@ class AudioStreamPlayer : public Node {
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List *p_list) const;
+ void _play_internal(Ref stream_playback, double p_from_pos = 0.0);
+
#ifndef DISABLE_DEPRECATED
bool _is_autoplay_enabled_bind_compat_86907();
static void _bind_compatibility_methods();
diff --git a/scene/audio/audio_stream_player_internal.cpp b/scene/audio/audio_stream_player_internal.cpp
index f738530f0948..e404742ed629 100644
--- a/scene/audio/audio_stream_player_internal.cpp
+++ b/scene/audio/audio_stream_player_internal.cpp
@@ -31,6 +31,7 @@
#include "audio_stream_player_internal.h"
#include "scene/main/node.h"
+#include "scene/resources/audio_stream_playback_scheduled.h"
#include "servers/audio/audio_stream.h"
void AudioStreamPlayerInternal::_set_process(bool p_enabled) {
@@ -133,7 +134,7 @@ void AudioStreamPlayerInternal::notification(int p_what) {
}
}
-Ref AudioStreamPlayerInternal::play_basic() {
+Ref AudioStreamPlayerInternal::_create_playback() {
Ref stream_playback;
if (stream.is_null()) {
return stream_playback;
@@ -168,12 +169,41 @@ Ref AudioStreamPlayerInternal::play_basic() {
}
}
+ return stream_playback;
+}
+
+Ref AudioStreamPlayerInternal::play_basic() {
+ Ref stream_playback = _create_playback();
+ if (stream_playback.is_null()) {
+ return stream_playback;
+ }
+
stream_playbacks.push_back(stream_playback);
active.set();
_set_process(true);
return stream_playback;
}
+Ref AudioStreamPlayerInternal::play_scheduled_basic() {
+ Ref stream_playback = _create_playback();
+ if (stream_playback.is_null()) {
+ return Ref();
+ }
+
+ Ref stream_playback_scheduled;
+ stream_playback_scheduled.instantiate();
+ stream_playback_scheduled->set_base_playback(stream_playback);
+
+ if (stream_playback_scheduled->get_is_sample()) {
+ WARN_PRINT_ED("Scheduled play does not support samples. Playing immediately.");
+ }
+
+ stream_playbacks.push_back(stream_playback_scheduled);
+ active.set();
+ _set_process(true);
+ return stream_playback_scheduled;
+}
+
void AudioStreamPlayerInternal::set_stream_paused(bool p_pause) {
// TODO this does not have perfect recall, fix that maybe? If there are zero playbacks registered with the AudioServer, this bool isn't persisted.
for (Ref &playback : stream_playbacks) {
@@ -263,7 +293,7 @@ void AudioStreamPlayerInternal::set_stream(Ref p_stream) {
node->notify_property_list_changed();
}
-void AudioStreamPlayerInternal::seek(float p_seconds) {
+void AudioStreamPlayerInternal::seek(double p_seconds) {
if (is_playing()) {
stop_callable.call();
play_callable.call(p_seconds);
@@ -289,7 +319,7 @@ bool AudioStreamPlayerInternal::is_playing() const {
return false;
}
-float AudioStreamPlayerInternal::get_playback_position() {
+double AudioStreamPlayerInternal::get_playback_position() {
// Return the playback position of the most recently started playback stream.
if (!stream_playbacks.is_empty()) {
return AudioServer::get_singleton()->get_playback_position(stream_playbacks[stream_playbacks.size() - 1]);
diff --git a/scene/audio/audio_stream_player_internal.h b/scene/audio/audio_stream_player_internal.h
index bd7075d8514b..f4b0e67256c3 100644
--- a/scene/audio/audio_stream_player_internal.h
+++ b/scene/audio/audio_stream_player_internal.h
@@ -36,6 +36,7 @@
class AudioStream;
class AudioStreamPlayback;
+class AudioStreamPlaybackScheduled;
class AudioSamplePlayback;
class Node;
@@ -61,6 +62,8 @@ class AudioStreamPlayerInternal : public Object {
void _set_process(bool p_enabled);
void _update_stream_parameters();
+ Ref _create_playback();
+
_FORCE_INLINE_ bool _is_sample() {
return (AudioServer::get_singleton()->get_default_playback_type() == AudioServer::PlaybackType::PLAYBACK_TYPE_SAMPLE && get_playback_type() == AudioServer::PlaybackType::PLAYBACK_TYPE_DEFAULT) || get_playback_type() == AudioServer::PlaybackType::PLAYBACK_TYPE_SAMPLE;
}
@@ -93,10 +96,11 @@ class AudioStreamPlayerInternal : public Object {
StringName get_bus() const;
Ref play_basic();
- void seek(float p_seconds);
+ Ref play_scheduled_basic();
+ void seek(double p_seconds);
void stop_basic();
bool is_playing() const;
- float get_playback_position();
+ double get_playback_position();
void set_playing(bool p_enable);
bool is_active() const;
diff --git a/scene/audio/scheduled_audio_stream_player.cpp b/scene/audio/scheduled_audio_stream_player.cpp
new file mode 100644
index 000000000000..00debc672061
--- /dev/null
+++ b/scene/audio/scheduled_audio_stream_player.cpp
@@ -0,0 +1,49 @@
+/**************************************************************************/
+/* scheduled_audio_stream_player.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "scheduled_audio_stream_player.h"
+
+#include "scene/audio/audio_stream_player_internal.h"
+#include "scene/resources/audio_stream_playback_scheduled.h"
+
+Ref ScheduledAudioStreamPlayer::play_scheduled(double p_abs_time, double p_from_pos) {
+ Ref stream_playback_scheduled = internal->play_scheduled_basic();
+ if (stream_playback_scheduled.is_null()) {
+ return stream_playback_scheduled;
+ }
+ stream_playback_scheduled->set_scheduled_start_time(p_abs_time);
+ _play_internal(stream_playback_scheduled, p_from_pos);
+
+ return stream_playback_scheduled;
+}
+
+void ScheduledAudioStreamPlayer::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("play_scheduled", "absolute_time", "from_position"), &ScheduledAudioStreamPlayer::play_scheduled, DEFVAL(0.0));
+}
diff --git a/scene/audio/scheduled_audio_stream_player.h b/scene/audio/scheduled_audio_stream_player.h
new file mode 100644
index 000000000000..a3829fdd80f7
--- /dev/null
+++ b/scene/audio/scheduled_audio_stream_player.h
@@ -0,0 +1,45 @@
+/**************************************************************************/
+/* scheduled_audio_stream_player.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#pragma once
+
+#include "audio_stream_player.h"
+
+class AudioStreamPlaybackScheduled;
+
+class ScheduledAudioStreamPlayer : public AudioStreamPlayer {
+ GDCLASS(ScheduledAudioStreamPlayer, AudioStreamPlayer);
+
+protected:
+ static void _bind_methods();
+
+public:
+ Ref play_scheduled(double p_abs_time, double p_from_pos = 0.0);
+};
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index da9d0fbc949a..19fb139dd143 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -43,6 +43,7 @@
#include "scene/animation/animation_tree.h"
#include "scene/animation/tween.h"
#include "scene/audio/audio_stream_player.h"
+#include "scene/audio/scheduled_audio_stream_player.h"
#include "scene/debugger/scene_debugger.h"
#include "scene/gui/aspect_ratio_container.h"
#include "scene/gui/box_container.h"
@@ -109,6 +110,7 @@
#include "scene/main/window.h"
#include "scene/resources/animation_library.h"
#include "scene/resources/atlas_texture.h"
+#include "scene/resources/audio_stream_playback_scheduled.h"
#include "scene/resources/audio_stream_polyphonic.h"
#include "scene/resources/audio_stream_wav.h"
#include "scene/resources/bit_map.h"
@@ -185,6 +187,7 @@
#include "scene/2d/path_2d.h"
#include "scene/2d/polygon_2d.h"
#include "scene/2d/remote_transform_2d.h"
+#include "scene/2d/scheduled_audio_stream_player_2d.h"
#include "scene/2d/skeleton_2d.h"
#include "scene/2d/sprite_2d.h"
#include "scene/2d/tile_map_layer.h"
@@ -252,6 +255,7 @@
#include "scene/3d/reflection_probe.h"
#include "scene/3d/remote_transform_3d.h"
#include "scene/3d/retarget_modifier_3d.h"
+#include "scene/3d/scheduled_audio_stream_player_3d.h"
#include "scene/3d/skeleton_3d.h"
#include "scene/3d/skeleton_modifier_3d.h"
#include "scene/3d/spline_ik_3d.h"
@@ -1004,6 +1008,7 @@ void register_scene_types() {
GDREGISTER_CLASS(RibbonTrailMesh);
GDREGISTER_CLASS(PointMesh);
GDREGISTER_ABSTRACT_CLASS(BaseMaterial3D);
+ GDREGISTER_CLASS(ScheduledAudioStreamPlayer3D);
GDREGISTER_CLASS(StandardMaterial3D);
GDREGISTER_CLASS(ORMMaterial3D);
GDREGISTER_CLASS(ProceduralSkyMaterial);
@@ -1121,6 +1126,8 @@ void register_scene_types() {
GDREGISTER_CLASS(AudioStreamWAV);
GDREGISTER_CLASS(AudioStreamPolyphonic);
GDREGISTER_ABSTRACT_CLASS(AudioStreamPlaybackPolyphonic);
+ GDREGISTER_CLASS(AudioStreamPlaybackScheduled);
+ GDREGISTER_CLASS(ScheduledAudioStreamPlayer);
OS::get_singleton()->yield(); // may take time to init
@@ -1128,6 +1135,7 @@ void register_scene_types() {
GDREGISTER_CLASS(Curve2D);
GDREGISTER_CLASS(Path2D);
GDREGISTER_CLASS(PathFollow2D);
+ GDREGISTER_CLASS(ScheduledAudioStreamPlayer2D);
#ifndef PHYSICS_2D_DISABLED
GDREGISTER_ABSTRACT_CLASS(Shape2D);
diff --git a/scene/resources/audio_stream_playback_scheduled.cpp b/scene/resources/audio_stream_playback_scheduled.cpp
new file mode 100644
index 000000000000..cf8b119fc161
--- /dev/null
+++ b/scene/resources/audio_stream_playback_scheduled.cpp
@@ -0,0 +1,249 @@
+/**************************************************************************/
+/* audio_stream_playback_scheduled.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "audio_stream_playback_scheduled.h"
+
+#include "servers/audio/audio_server.h"
+
+void AudioStreamPlaybackScheduled::start(double p_from_pos) {
+ ERR_FAIL_COND_MSG(base_playback.is_null(), "base_playback must be set.");
+
+ active = true;
+ base_playback->start(p_from_pos);
+
+ uint64_t mix_start = AudioServer::get_singleton()->get_mixed_frames();
+ if (mix_start > scheduled_start_frame) {
+ WARN_PRINT_ED(vformat("Sound (%s) was scheduled for absolute time %.4f, which has already passed. Playing immediately.",
+ get_instance_id(),
+ get_scheduled_start_time()));
+ }
+}
+
+void AudioStreamPlaybackScheduled::stop() {
+ active = false;
+ if (base_playback.is_null()) {
+ return;
+ }
+ base_playback->stop();
+}
+
+bool AudioStreamPlaybackScheduled::is_playing() const {
+ if (base_playback.is_null()) {
+ return false;
+ }
+ uint64_t mix_start = AudioServer::get_singleton()->get_mixed_frames();
+ if (mix_start < scheduled_start_frame) {
+ return false;
+ }
+ return base_playback->is_playing();
+}
+
+int AudioStreamPlaybackScheduled::get_loop_count() const {
+ if (base_playback.is_null()) {
+ return 0;
+ }
+ return base_playback->get_loop_count();
+}
+
+double AudioStreamPlaybackScheduled::get_playback_position() const {
+ if (base_playback.is_null()) {
+ return 0.0;
+ }
+ return base_playback->get_playback_position();
+}
+
+void AudioStreamPlaybackScheduled::seek(double p_time) {
+ if (base_playback.is_null()) {
+ return;
+ }
+ base_playback->seek(p_time);
+}
+
+void AudioStreamPlaybackScheduled::tag_used_streams() {
+ if (base_playback.is_null()) {
+ return;
+ }
+ base_playback->tag_used_streams();
+}
+
+void AudioStreamPlaybackScheduled::set_parameter(const StringName &p_name, const Variant &p_value) {
+ if (base_playback.is_null()) {
+ return;
+ }
+ base_playback->set_parameter(p_name, p_value);
+}
+
+Variant AudioStreamPlaybackScheduled::get_parameter(const StringName &p_name) const {
+ if (base_playback.is_null()) {
+ return Variant();
+ }
+ return base_playback->get_parameter(p_name);
+}
+
+int AudioStreamPlaybackScheduled::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) {
+ // Pre-clear buffer.
+ for (int i = 0; i < p_frames; i++) {
+ p_buffer[i] = AudioFrame(0, 0);
+ }
+
+ if (!active) {
+ return 0;
+ }
+ if (base_playback.is_null()) {
+ return 0;
+ }
+ ERR_FAIL_COND_V_MSG(scheduled_end_frame > 0 && scheduled_end_frame < scheduled_start_frame, 0, "Scheduled end time is before scheduled start time.");
+
+ AudioFrame *buf = p_buffer;
+ unsigned int todo = p_frames;
+ uint64_t mix_start = AudioServer::get_singleton()->get_mixed_frames();
+ uint64_t mix_end = mix_start + p_frames;
+
+ // Fill buffer with silence frames if sound isn't scheduled to start yet.
+ if (mix_start < scheduled_start_frame) {
+ unsigned int silence_frames = (unsigned int)MIN(scheduled_start_frame - mix_start, todo);
+ for (unsigned int i = 0; i < silence_frames; i++) {
+ buf[i] = AudioFrame(0, 0);
+ }
+ todo -= silence_frames;
+ buf += silence_frames;
+
+ if (todo == 0) {
+ return p_frames;
+ }
+ }
+
+ // Limit the amount of mixing if the sound is scheduled to end within this
+ // chunk.
+ if (scheduled_end_frame > 0 && scheduled_end_frame < mix_end) {
+ unsigned int frames_to_skip = (unsigned int)MIN(mix_end - scheduled_end_frame, todo);
+ todo -= base_playback->mix(buf, p_rate_scale, todo - frames_to_skip);
+ stop();
+ return p_frames - todo;
+ }
+
+ // Normal mixing.
+ todo -= base_playback->mix(buf, p_rate_scale, todo);
+
+ return p_frames - todo;
+}
+
+void AudioStreamPlaybackScheduled::set_is_sample(bool p_is_sample) {
+ if (base_playback.is_null()) {
+ return;
+ }
+ base_playback->set_is_sample(p_is_sample);
+}
+
+bool AudioStreamPlaybackScheduled::get_is_sample() const {
+ if (base_playback.is_null()) {
+ return false;
+ }
+ return base_playback->get_is_sample();
+}
+
+void AudioStreamPlaybackScheduled::set_sample_playback(const Ref &p_playback) {
+ if (base_playback.is_null()) {
+ return;
+ }
+ base_playback->set_sample_playback(p_playback);
+}
+
+Ref AudioStreamPlaybackScheduled::get_sample_playback() const {
+ if (base_playback.is_null()) {
+ return Ref();
+ }
+ return base_playback->get_sample_playback();
+}
+
+void AudioStreamPlaybackScheduled::cancel() {
+ uint64_t mix_start = AudioServer::get_singleton()->get_mixed_frames();
+ if (mix_start < scheduled_start_frame) {
+ stop();
+ }
+}
+
+bool AudioStreamPlaybackScheduled::is_scheduled() const {
+ uint64_t mix_start = AudioServer::get_singleton()->get_mixed_frames();
+ return active && mix_start < scheduled_start_frame;
+}
+
+void AudioStreamPlaybackScheduled::set_scheduled_start_time(double p_start_time) {
+ scheduled_start_frame = int64_t(p_start_time * AudioServer::get_singleton()->get_mix_rate());
+
+ if (active) {
+ uint64_t mix_start = AudioServer::get_singleton()->get_mixed_frames();
+ if (mix_start > scheduled_start_frame) {
+ WARN_PRINT_ED(vformat("Sound (%s) was scheduled for absolute time %.4f, which has already passed. Playing immediately.",
+ get_instance_id(),
+ get_scheduled_start_time()));
+ }
+ }
+}
+
+double AudioStreamPlaybackScheduled::get_scheduled_start_time() const {
+ return scheduled_start_frame / AudioServer::get_singleton()->get_mix_rate();
+}
+
+void AudioStreamPlaybackScheduled::set_scheduled_end_time(double p_end_time) {
+ scheduled_end_frame = int64_t(p_end_time * AudioServer::get_singleton()->get_mix_rate());
+}
+
+double AudioStreamPlaybackScheduled::get_scheduled_end_time() const {
+ return scheduled_end_frame / AudioServer::get_singleton()->get_mix_rate();
+}
+
+void AudioStreamPlaybackScheduled::set_base_playback(const Ref &p_playback) {
+ ERR_FAIL_COND_MSG(p_playback == this, "Cannot assign base_playback to self.");
+ stop();
+ base_playback = p_playback;
+}
+
+Ref AudioStreamPlaybackScheduled::get_base_playback() const {
+ return base_playback;
+}
+
+void AudioStreamPlaybackScheduled::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("cancel"), &AudioStreamPlaybackScheduled::cancel);
+ ClassDB::bind_method(D_METHOD("is_scheduled"), &AudioStreamPlaybackScheduled::is_scheduled);
+
+ ClassDB::bind_method(D_METHOD("set_base_playback", "base_playback"), &AudioStreamPlaybackScheduled::set_base_playback);
+ ClassDB::bind_method(D_METHOD("get_base_playback"), &AudioStreamPlaybackScheduled::get_base_playback);
+
+ ClassDB::bind_method(D_METHOD("set_scheduled_start_time", "start_time"), &AudioStreamPlaybackScheduled::set_scheduled_start_time);
+ ClassDB::bind_method(D_METHOD("get_scheduled_start_time"), &AudioStreamPlaybackScheduled::get_scheduled_start_time);
+
+ ClassDB::bind_method(D_METHOD("set_scheduled_end_time", "end_time"), &AudioStreamPlaybackScheduled::set_scheduled_end_time);
+ ClassDB::bind_method(D_METHOD("get_scheduled_end_time"), &AudioStreamPlaybackScheduled::get_scheduled_end_time);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "base_playback", PROPERTY_HINT_RESOURCE_TYPE, "AudioStreamPlayback"), "set_base_playback", "get_base_playback");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scheduled_start_time"), "set_scheduled_start_time", "get_scheduled_start_time");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scheduled_end_time"), "set_scheduled_end_time", "get_scheduled_end_time");
+}
diff --git a/scene/resources/audio_stream_playback_scheduled.h b/scene/resources/audio_stream_playback_scheduled.h
new file mode 100644
index 000000000000..83f4747b43fe
--- /dev/null
+++ b/scene/resources/audio_stream_playback_scheduled.h
@@ -0,0 +1,83 @@
+/**************************************************************************/
+/* audio_stream_playback_scheduled.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#pragma once
+
+#include "servers/audio/audio_stream.h"
+
+class AudioStreamPlaybackScheduled : public AudioStreamPlayback {
+ GDCLASS(AudioStreamPlaybackScheduled, AudioStreamPlayback);
+
+ Ref base_playback;
+
+ uint64_t scheduled_start_frame = 0;
+ uint64_t scheduled_end_frame = 0;
+
+ bool active = false;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void start(double p_from_pos = 0.0) override;
+ virtual void stop() override;
+ virtual bool is_playing() const override;
+
+ virtual int get_loop_count() const override; //times it looped
+
+ virtual double get_playback_position() const override;
+ virtual void seek(double p_time) override;
+
+ virtual void tag_used_streams() override;
+
+ virtual void set_parameter(const StringName &p_name, const Variant &p_value) override;
+ virtual Variant get_parameter(const StringName &p_name) const override;
+
+ virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override;
+
+ virtual void set_is_sample(bool p_is_sample) override;
+ virtual bool get_is_sample() const override;
+ virtual void set_sample_playback(const Ref &p_playback) override;
+ virtual Ref get_sample_playback() const override;
+
+ void cancel();
+ bool is_scheduled() const;
+
+ void set_base_playback(const Ref &p_playback);
+ Ref get_base_playback() const;
+
+ void set_scheduled_start_time(double p_start_time);
+ double get_scheduled_start_time() const;
+
+ void set_scheduled_end_time(double p_end_time);
+ double get_scheduled_end_time() const;
+
+ AudioStreamPlaybackScheduled() {}
+};
diff --git a/servers/audio/audio_server.cpp b/servers/audio/audio_server.cpp
index fa6715dec9ec..a676fa9be41d 100644
--- a/servers/audio/audio_server.cpp
+++ b/servers/audio/audio_server.cpp
@@ -1225,21 +1225,21 @@ float AudioServer::get_playback_speed_scale() const {
return playback_speed_scale;
}
-void AudioServer::start_playback_stream(Ref p_playback, const StringName &p_bus, Vector p_volume_db_vector, float p_start_time, float p_pitch_scale) {
+void AudioServer::start_playback_stream(Ref p_playback, const StringName &p_bus, Vector p_volume_db_vector, double p_from_pos, float p_pitch_scale) {
ERR_FAIL_COND(p_playback.is_null());
HashMap> map;
map[p_bus] = p_volume_db_vector;
- start_playback_stream(p_playback, map, p_start_time, p_pitch_scale);
+ start_playback_stream(p_playback, map, p_from_pos, p_pitch_scale);
}
-void AudioServer::start_playback_stream(Ref p_playback, const HashMap> &p_bus_volumes, float p_start_time, float p_pitch_scale, float p_highshelf_gain, float p_attenuation_cutoff_hz) {
+void AudioServer::start_playback_stream(Ref p_playback, const HashMap> &p_bus_volumes, double p_from_pos, float p_pitch_scale, float p_highshelf_gain, float p_attenuation_cutoff_hz) {
ERR_FAIL_COND(p_playback.is_null());
AudioStreamPlaybackListNode *playback_node = new AudioStreamPlaybackListNode();
playback_node->stream_playback = p_playback;
- playback_node->stream_playback->start(p_start_time);
+ playback_node->stream_playback->start(p_from_pos);
AudioStreamPlaybackBusDetails *new_bus_details = new AudioStreamPlaybackBusDetails();
int idx = 0;
@@ -1448,7 +1448,7 @@ bool AudioServer::is_playback_active(Ref p_playback) {
return playback_node->state.load() == AudioStreamPlaybackListNode::PLAYING;
}
-float AudioServer::get_playback_position(Ref p_playback) {
+double AudioServer::get_playback_position(Ref p_playback) {
ERR_FAIL_COND_V(p_playback.is_null(), 0);
// Samples.
@@ -1688,6 +1688,10 @@ double AudioServer::get_time_since_last_mix() const {
return AudioDriver::get_singleton()->get_time_since_last_mix();
}
+double AudioServer::get_absolute_time() const {
+ return mix_frames / double(get_mix_rate());
+}
+
AudioServer *AudioServer::singleton = nullptr;
void AudioServer::add_update_callback(AudioCallback p_callback, void *p_userdata) {
@@ -2079,6 +2083,7 @@ void AudioServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_time_to_next_mix"), &AudioServer::get_time_to_next_mix);
ClassDB::bind_method(D_METHOD("get_time_since_last_mix"), &AudioServer::get_time_since_last_mix);
+ ClassDB::bind_method(D_METHOD("get_absolute_time"), &AudioServer::get_absolute_time);
ClassDB::bind_method(D_METHOD("get_output_latency"), &AudioServer::get_output_latency);
ClassDB::bind_method(D_METHOD("get_input_device_list"), &AudioServer::get_input_device_list);
diff --git a/servers/audio/audio_server.h b/servers/audio/audio_server.h
index a7a4decf8357..721c689f0fd3 100644
--- a/servers/audio/audio_server.h
+++ b/servers/audio/audio_server.h
@@ -431,9 +431,9 @@ class AudioServer : public Object {
float get_playback_speed_scale() const;
// Convenience method.
- void start_playback_stream(Ref p_playback, const StringName &p_bus, Vector p_volume_db_vector, float p_start_time = 0, float p_pitch_scale = 1);
+ void start_playback_stream(Ref p_playback, const StringName &p_bus, Vector p_volume_db_vector, double p_from_pos = 0, float p_pitch_scale = 1);
// Expose all parameters.
- void start_playback_stream(Ref p_playback, const HashMap> &p_bus_volumes, float p_start_time = 0, float p_pitch_scale = 1, float p_highshelf_gain = 0, float p_attenuation_cutoff_hz = 0);
+ void start_playback_stream(Ref p_playback, const HashMap> &p_bus_volumes, double p_from_pos = 0, float p_pitch_scale = 1, float p_highshelf_gain = 0, float p_attenuation_cutoff_hz = 0);
void stop_playback_stream(Ref p_playback);
void set_playback_bus_exclusive(Ref p_playback, const StringName &p_bus, Vector p_volumes);
@@ -444,7 +444,7 @@ class AudioServer : public Object {
void set_playback_highshelf_params(Ref p_playback, float p_gain, float p_attenuation_cutoff_hz);
bool is_playback_active(Ref p_playback);
- float get_playback_position(Ref p_playback);
+ double get_playback_position(Ref p_playback);
bool is_playback_paused(Ref p_playback);
uint64_t get_mix_count() const;
@@ -475,6 +475,7 @@ class AudioServer : public Object {
virtual double get_output_latency() const;
virtual double get_time_to_next_mix() const;
virtual double get_time_since_last_mix() const;
+ virtual double get_absolute_time() const;
void add_listener_changed_callback(AudioCallback p_callback, void *p_userdata);
void remove_listener_changed_callback(AudioCallback p_callback, void *p_userdata);