Skip to content

Commit 9294d58

Browse files
PizzaLovers007fire
andcommitted
v2 Implement audio absolute time and scheduled play
This is a rewrite of godotengine#105510 that moves the silence frames logic into a separate AudioStreamPlaybackScheduled class and introduces new ScheduledAudioStreamPlayer/2D/3D nodes. The rewrite allows both the AudioServer and the player (mostly) to treat it as if it were a generic playback. Main differences: - ScheduledAudioStreamPlayer/2D/3D are new nodes inheriting from their AudioStreamPlayer/2D/3D counterparts. These nodes only contain one new method: play_scheduled. - play_scheduled returns an AudioStreamPlaybackScheduled instance, which is tied to the player that created it. - The start time can be changed after scheduling. - You can now set an end time for the playback. - The scheduled playback can be cancelled separately from other playbacks on the player. Co-authored-by: K. S. Ernest (iFire) Lee <ernest.lee@chibifire.com>
1 parent 26df043 commit 9294d58

27 files changed

+865
-33
lines changed

doc/classes/AudioServer.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@
3535
Generates an [AudioBusLayout] using the available buses and effects.
3636
</description>
3737
</method>
38+
<method name="get_absolute_time" qualifiers="const" experimental="">
39+
<return type="float" />
40+
<description>
41+
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].
42+
See also [AudioStreamPlaybackScheduled].
43+
[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].
44+
</description>
45+
</method>
3846
<method name="get_bus_channels" qualifiers="const">
3947
<return type="int" />
4048
<param index="0" name="bus_idx" type="int" />
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<class name="AudioStreamPlaybackScheduled" inherits="AudioStreamPlayback" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
3+
<brief_description>
4+
Wrapper around [AudioStreamPlayback] that schedules it to be played in the future.
5+
</brief_description>
6+
<description>
7+
Wrapper around [AudioStreamPlayback] that schedules it to be played in the future.
8+
An [AudioStreamPlaybackScheduled] instance is created from [method ScheduledAudioStreamPlayer.play_scheduled], [method ScheduledAudioStreamPlayer2D.play_scheduled], or [method ScheduledAudioStreamPlayer3D.play_scheduled].
9+
[codeblocks]
10+
[gdscript]
11+
var audio_time = AudioServer.get_absolute_time()
12+
13+
# Play the sound roughly 1 second in the future, then stop it after exactly 3 seconds.
14+
var playback_scheduled = player.play_scheduled(audio_time + 1)
15+
playback_scheduled.scheduled_end_time = audio_time + 4
16+
17+
# Cancel the playback
18+
playback_scheduled.cancel()
19+
[/gdscript]
20+
[csharp]
21+
double audioTime = AudioServer.GetAbsoluteTime();
22+
23+
// Play the sound roughly 1 second in the future, then stop it after exactly 3 seconds.
24+
AudioStreamPlaybackScheduled playbackScheduled = player.PlayScheduled(audioTime + 1);
25+
playbackScheduled.ScheduledEndTime = audioTime + 4;
26+
27+
// Cancel the playback
28+
playbackScheduled.Cancel();
29+
[/csharp]
30+
[/codeblocks]
31+
</description>
32+
<tutorials>
33+
</tutorials>
34+
<methods>
35+
<method name="cancel">
36+
<return type="void" />
37+
<description>
38+
Cancels the currently scheduled playback.
39+
If the playback is actively playing, this method does nothing. Use [method AudioStreamPlayback.stop] instead to both stop and cancel the scheduled playback.
40+
</description>
41+
</method>
42+
<method name="is_scheduled" qualifiers="const">
43+
<return type="bool" />
44+
<description>
45+
Returns [code]true[/code] if the playback is scheduled and has not yet started.
46+
</description>
47+
</method>
48+
</methods>
49+
<members>
50+
<member name="base_playback" type="AudioStreamPlayback" setter="set_base_playback" getter="get_base_playback">
51+
The wrapped [AudioStreamPlayback] to be scheduled. If set, the previously scheduled playback will be cancelled/stopped.
52+
</member>
53+
<member name="scheduled_end_time" type="float" setter="set_scheduled_end_time" getter="get_scheduled_end_time" default="0.0">
54+
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]).
55+
</member>
56+
<member name="scheduled_start_time" type="float" setter="set_scheduled_start_time" getter="get_scheduled_start_time" default="0.0">
57+
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]).
58+
</member>
59+
</members>
60+
</class>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<class name="ScheduledAudioStreamPlayer" inherits="AudioStreamPlayer" experimental="" keywords="sound, music, song" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
3+
<brief_description>
4+
A node for scheduled audio playback.
5+
</brief_description>
6+
<description>
7+
Behaves similarly to [AudioStreamPlayer] but can schedule audio to play in the future.
8+
If you need to play scheduled audio at a specific position, use [ScheduledAudioStreamPlayer2D] or [ScheduledAudioStreamPlayer3D] instead.
9+
</description>
10+
<tutorials>
11+
</tutorials>
12+
<methods>
13+
<method name="play_scheduled">
14+
<return type="AudioStreamPlaybackScheduled" />
15+
<param index="0" name="absolute_time" type="float" />
16+
<param index="1" name="from_position" type="float" default="0.0" />
17+
<description>
18+
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.
19+
Use this method for high precision playbacks, such as a metronome or other rhythm-based sounds.
20+
Returns an [AudioStreamPlaybackScheduled] instance representing the scheduled playback of the sound.
21+
[b]Note:[/b] Calling this method after [member AudioStreamPlayer.max_polyphony] is reached will cut off the oldest sound playing on this node.
22+
[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].
23+
</description>
24+
</method>
25+
</methods>
26+
</class>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<class name="ScheduledAudioStreamPlayer2D" inherits="AudioStreamPlayer2D" experimental="" keywords="sound, music, song" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
3+
<brief_description>
4+
A node for scheduled audio playback.
5+
</brief_description>
6+
<description>
7+
Behaves similarly to [AudioStreamPlayer2D] but can schedule audio to play in the future.
8+
See also [ScheduledAudioStreamPlayer] to play a scheduled sound non-positionally.
9+
</description>
10+
<tutorials>
11+
</tutorials>
12+
<methods>
13+
<method name="play_scheduled">
14+
<return type="AudioStreamPlaybackScheduled" />
15+
<param index="0" name="absolute_time" type="float" />
16+
<param index="1" name="from_position" type="float" default="0.0" />
17+
<description>
18+
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.
19+
Use this method for high precision playbacks, such as a metronome or other rhythm-based sounds.
20+
Returns an [AudioStreamPlaybackScheduled] instance representing the scheduled playback of the sound.
21+
[b]Note:[/b] Calling this method after [member AudioStreamPlayer2D.max_polyphony] is reached will cut off the oldest sound playing on this node.
22+
[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].
23+
</description>
24+
</method>
25+
</methods>
26+
</class>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<class name="ScheduledAudioStreamPlayer3D" inherits="AudioStreamPlayer3D" experimental="" keywords="sound, music, song" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
3+
<brief_description>
4+
A node for scheduled audio playback.
5+
</brief_description>
6+
<description>
7+
Behaves similarly to [AudioStreamPlayer3D] but can schedule audio to play in the future.
8+
See also [ScheduledAudioStreamPlayer] to play a scheduled sound non-positionally.
9+
</description>
10+
<tutorials>
11+
</tutorials>
12+
<methods>
13+
<method name="play_scheduled">
14+
<return type="AudioStreamPlaybackScheduled" />
15+
<param index="0" name="absolute_time" type="float" />
16+
<param index="1" name="from_position" type="float" default="0.0" />
17+
<description>
18+
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.
19+
Use this method for high precision playbacks, such as a metronome or other rhythm-based sounds.
20+
Returns an [AudioStreamPlaybackScheduled] instance representing the scheduled playback of the sound.
21+
[b]Note:[/b] Calling this method after [member AudioStreamPlayer3D.max_polyphony] is reached will cut off the oldest sound playing on this node.
22+
[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].
23+
</description>
24+
</method>
25+
</methods>
26+
</class>
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 1 addition & 0 deletions
Loading

scene/2d/audio_stream_player_2d.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -231,11 +231,7 @@ float AudioStreamPlayer2D::get_pitch_scale() const {
231231
return internal->pitch_scale;
232232
}
233233

234-
void AudioStreamPlayer2D::play(float p_from_pos) {
235-
Ref<AudioStreamPlayback> stream_playback = internal->play_basic();
236-
if (stream_playback.is_null()) {
237-
return;
238-
}
234+
void AudioStreamPlayer2D::_play_internal(Ref<AudioStreamPlayback> stream_playback, double p_from_pos) {
239235
setplayback = stream_playback;
240236
setplay.set(p_from_pos);
241237

@@ -249,6 +245,14 @@ void AudioStreamPlayer2D::play(float p_from_pos) {
249245
}
250246
}
251247

248+
void AudioStreamPlayer2D::play(float p_from_pos) {
249+
Ref<AudioStreamPlayback> stream_playback = internal->play_basic();
250+
if (stream_playback.is_null()) {
251+
return;
252+
}
253+
_play_internal(stream_playback, p_from_pos);
254+
}
255+
252256
void AudioStreamPlayer2D::seek(float p_seconds) {
253257
internal->seek(p_seconds);
254258
}

scene/2d/audio_stream_player_2d.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ class AudioStreamPlayerInternal;
4141
class AudioStreamPlayer2D : public Node2D {
4242
GDCLASS(AudioStreamPlayer2D, Node2D);
4343

44+
protected:
45+
AudioStreamPlayerInternal *internal = nullptr;
46+
4447
private:
4548
enum {
4649
MAX_OUTPUTS = 8,
@@ -54,8 +57,6 @@ class AudioStreamPlayer2D : public Node2D {
5457
Viewport *viewport = nullptr; //pointer only used for reference to previous mix
5558
};
5659

57-
AudioStreamPlayerInternal *internal = nullptr;
58-
5960
SafeNumeric<float> setplay{ -1.0 };
6061
Ref<AudioStreamPlayback> setplayback;
6162

@@ -91,6 +92,8 @@ class AudioStreamPlayer2D : public Node2D {
9192
bool _get(const StringName &p_name, Variant &r_ret) const;
9293
void _get_property_list(List<PropertyInfo> *p_list) const;
9394

95+
void _play_internal(Ref<AudioStreamPlayback> stream_playback, double p_from_pos = 0.0);
96+
9497
#ifndef DISABLE_DEPRECATED
9598
bool _is_autoplay_enabled_bind_compat_86907();
9699
static void _bind_compatibility_methods();

0 commit comments

Comments
 (0)