Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions doc/classes/AudioServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,26 @@
Generates an [AudioBusLayout] using the available buses and effects.
</description>
</method>
<method name="get_absolute_time" qualifiers="const" experimental="">
<return type="float" />
<description>
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 AudioStreamPlayer.play_scheduled].
[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].
[b]Example:[/b] Schedule two sounds to be played at the same time, roughly 1 second in the future:
[codeblocks]
[gdscript]
var future_time = AudioServer.get_absolute_time() + 1
player1.play_scheduled(future_time)
player2.play_scheduled(future_time)
[/gdscript]
[csharp]
double futureTime = AudioServer.GetAbsoluteTime() + 1;
player1.PlayScheduled(futureTime);
player2.PlayScheduled(futureTime);
[/csharp]
[/codeblocks]
</description>
</method>
<method name="get_bus_channels" qualifiers="const">
<return type="int" />
<param index="0" name="bus_idx" type="int" />
Expand Down
15 changes: 13 additions & 2 deletions doc/classes/AudioStreamPlayer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
<method name="get_stream_playback">
<return type="AudioStreamPlayback" />
<description>
Returns the latest [AudioStreamPlayback] of this node, usually the most recently created by [method play]. If no sounds are playing, this method fails and returns an empty playback.
Returns the latest [AudioStreamPlayback] of this node, usually the most recently created by [method play] or [method play_scheduled]. If no sounds are playing, this method fails and returns an empty playback.
</description>
</method>
<method name="has_stream_playback">
Expand All @@ -44,6 +44,17 @@
Plays a sound from the beginning, or the given [param from_position] in seconds.
</description>
</method>
<method name="play_scheduled" experimental="">
<return type="void" />
<param index="0" name="absolute_time" type="float" />
<param index="1" name="from_position" type="float" default="0.0" />
<description>
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.
[b]Note:[/b] Calling this method after [member max_polyphony] is reached will cut off the oldest sound playing on this node.
[b]Note:[/b] On the Web platform, [member playback_type] must be set to [constant AudioServer.PLAYBACK_TYPE_STREAM]. Otherwise, this method will behave like [method play].
</description>
</method>
<method name="seek">
<return type="void" />
<param index="0" name="to_position" type="float" />
Expand All @@ -67,7 +78,7 @@
[b]Note:[/b] At runtime, if no bus with the given name exists, all sounds will fall back on [code]"Master"[/code]. See also [method AudioServer.get_bus_name].
</member>
<member name="max_polyphony" type="int" setter="set_max_polyphony" getter="get_max_polyphony" default="1">
The maximum number of sounds this node can play at the same time. Calling [method play] after this value is reached will cut off the oldest sounds.
The maximum number of sounds this node can play and schedule at the same time. Calling [method play] or [method play_scheduled] after this value is reached will cut off the oldest sounds.
</member>
<member name="mix_target" type="int" setter="set_mix_target" getter="get_mix_target" enum="AudioStreamPlayer.MixTarget" default="0">
The mix target channels, as one of the [enum MixTarget] constants. Has no effect when two speakers or less are detected (see [enum AudioServer.SpeakerMode]).
Expand Down
13 changes: 12 additions & 1 deletion doc/classes/AudioStreamPlayer2D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@
Queues the audio to play on the next physics frame, from the given position [param from_position], in seconds.
</description>
</method>
<method name="play_scheduled" experimental="">
<return type="void" />
<param index="0" name="absolute_time" type="float" />
<param index="1" name="from_position" type="float" default="0.0" />
<description>
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.
[b]Note:[/b] Calling this method after [member max_polyphony] is reached will cut off the oldest sound playing on this node.
[b]Note:[/b] On the Web platform, [member playback_type] must be set to [constant AudioServer.PLAYBACK_TYPE_STREAM]. Otherwise, this method will behave like [method play].
</description>
</method>
<method name="seek">
<return type="void" />
<param index="0" name="to_position" type="float" />
Expand Down Expand Up @@ -70,7 +81,7 @@
Maximum distance from which audio is still hearable.
</member>
<member name="max_polyphony" type="int" setter="set_max_polyphony" getter="get_max_polyphony" default="1">
The maximum number of sounds this node can play at the same time. Playing additional sounds after this value is reached will cut off the oldest sounds.
The maximum number of sounds this node can play and schedule at the same time. Calling [method play] or [method play_scheduled] after this value is reached will cut off the oldest sounds.
</member>
<member name="panning_strength" type="float" setter="set_panning_strength" getter="get_panning_strength" default="1.0">
Scales the panning strength for this node by multiplying the base [member ProjectSettings.audio/general/2d_panning_strength] with this factor. Higher values will pan audio from left to right more dramatically than lower values.
Expand Down
13 changes: 12 additions & 1 deletion doc/classes/AudioStreamPlayer3D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@
Queues the audio to play on the next physics frame, from the given position [param from_position], in seconds.
</description>
</method>
<method name="play_scheduled" experimental="">
<return type="void" />
<param index="0" name="absolute_time" type="float" />
<param index="1" name="from_position" type="float" default="0.0" />
<description>
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.
[b]Note:[/b] Calling this method after [member max_polyphony] is reached will cut off the oldest sound playing on this node.
[b]Note:[/b] On the Web platform, [member playback_type] must be set to [constant AudioServer.PLAYBACK_TYPE_STREAM]. Otherwise, this method will behave like [method play].
</description>
</method>
<method name="seek">
<return type="void" />
<param index="0" name="to_position" type="float" />
Expand Down Expand Up @@ -91,7 +102,7 @@
The distance past which the sound can no longer be heard at all. Only has an effect if set to a value greater than [code]0.0[/code]. [member max_distance] works in tandem with [member unit_size]. However, unlike [member unit_size] whose behavior depends on the [member attenuation_model], [member max_distance] always works in a linear fashion. This can be used to prevent the [AudioStreamPlayer3D] from requiring audio mixing when the listener is far away, which saves CPU resources.
</member>
<member name="max_polyphony" type="int" setter="set_max_polyphony" getter="get_max_polyphony" default="1">
The maximum number of sounds this node can play at the same time. Playing additional sounds after this value is reached will cut off the oldest sounds.
The maximum number of sounds this node can play and schedule at the same time. Calling [method play] or [method play_scheduled] after this value is reached will cut off the oldest sounds.
</member>
<member name="panning_strength" type="float" setter="set_panning_strength" getter="get_panning_strength" default="1.0">
Scales the panning strength for this node by multiplying the base [member ProjectSettings.audio/general/3d_panning_strength] by this factor. If the product is [code]0.0[/code] then stereo panning is disabled and the volume is the same for all channels. If the product is [code]1.0[/code] then one of the channels will be muted when the sound is located exactly to the left (or right) of the listener.
Expand Down
19 changes: 17 additions & 2 deletions scene/2d/audio_stream_player_2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ void AudioStreamPlayer2D::_notification(int p_what) {

if (setplayback.is_valid() && setplay.get() >= 0) {
internal->active.set();
AudioServer::get_singleton()->start_playback_stream(setplayback, _get_actual_bus(), volume_vector, setplay.get(), internal->pitch_scale);
AudioServer::get_singleton()->start_playback_stream(setplayback, _get_actual_bus(), volume_vector, setplay.get(), internal->scheduled_time, internal->pitch_scale);
setplayback.unref();
setplay.set(-1);
}
Expand Down Expand Up @@ -231,7 +231,7 @@ float AudioStreamPlayer2D::get_pitch_scale() const {
return internal->pitch_scale;
}

void AudioStreamPlayer2D::play(float p_from_pos) {
void AudioStreamPlayer2D::_play_internal(double p_from_pos) {
Ref<AudioStreamPlayback> stream_playback = internal->play_basic();
if (stream_playback.is_null()) {
return;
Expand All @@ -241,6 +241,10 @@ void AudioStreamPlayer2D::play(float p_from_pos) {

// Sample handling.
if (stream_playback->get_is_sample() && stream_playback->get_sample_playback().is_valid()) {
if (internal->scheduled_time > 0) {
WARN_PRINT_ED("play_scheduled() does not support samples. Playing immediately.");
}

Ref<AudioSamplePlayback> sample_playback = stream_playback->get_sample_playback();
sample_playback->offset = p_from_pos;
sample_playback->bus = _get_actual_bus();
Expand All @@ -249,6 +253,16 @@ void AudioStreamPlayer2D::play(float p_from_pos) {
}
}

void AudioStreamPlayer2D::play(float p_from_pos) {
internal->scheduled_time = 0;
_play_internal(p_from_pos);
}

void AudioStreamPlayer2D::play_scheduled(double p_abs_time, double p_from_pos) {
internal->scheduled_time = p_abs_time;
_play_internal(p_from_pos);
}

void AudioStreamPlayer2D::seek(float p_seconds) {
internal->seek(p_seconds);
}
Expand Down Expand Up @@ -388,6 +402,7 @@ void AudioStreamPlayer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_pitch_scale"), &AudioStreamPlayer2D::get_pitch_scale);

ClassDB::bind_method(D_METHOD("play", "from_position"), &AudioStreamPlayer2D::play, DEFVAL(0.0));
ClassDB::bind_method(D_METHOD("play_scheduled", "absolute_time", "from_position"), &AudioStreamPlayer2D::play_scheduled, DEFVAL(0.0));
ClassDB::bind_method(D_METHOD("seek", "to_position"), &AudioStreamPlayer2D::seek);
ClassDB::bind_method(D_METHOD("stop"), &AudioStreamPlayer2D::stop);

Expand Down
3 changes: 3 additions & 0 deletions scene/2d/audio_stream_player_2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ class AudioStreamPlayer2D : public Node2D {
StringName _get_actual_bus();
void _update_panning();

void _play_internal(double p_from_pos = 0.0);

static void _listener_changed_cb(void *self) { reinterpret_cast<AudioStreamPlayer2D *>(self)->force_update_panning = true; }

uint32_t area_mask = 1;
Expand Down Expand Up @@ -110,6 +112,7 @@ class AudioStreamPlayer2D : public Node2D {
float get_pitch_scale() const;

void play(float p_from_pos = 0.0);
void play_scheduled(double p_abs_time, double p_from_pos = 0.0);
void seek(float p_seconds);
void stop();
bool is_playing() const;
Expand Down
19 changes: 17 additions & 2 deletions scene/3d/audio_stream_player_3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ void AudioStreamPlayer3D::_notification(int p_what) {
internal->active.set();
HashMap<StringName, Vector<AudioFrame>> bus_map;
bus_map[_get_actual_bus()] = volume_vector;
AudioServer::get_singleton()->start_playback_stream(setplayback, bus_map, setplay.get(), actual_pitch_scale, linear_attenuation, attenuation_filter_cutoff_hz);
AudioServer::get_singleton()->start_playback_stream(setplayback, bus_map, setplay.get(), internal->scheduled_time, actual_pitch_scale, linear_attenuation, attenuation_filter_cutoff_hz);
setplayback.unref();
setplay.set(-1);
}
Expand Down Expand Up @@ -591,7 +591,7 @@ float AudioStreamPlayer3D::get_pitch_scale() const {
return internal->pitch_scale;
}

void AudioStreamPlayer3D::play(float p_from_pos) {
void AudioStreamPlayer3D::_play_internal(double p_from_pos) {
Ref<AudioStreamPlayback> stream_playback = internal->play_basic();
if (stream_playback.is_null()) {
return;
Expand All @@ -601,6 +601,10 @@ void AudioStreamPlayer3D::play(float p_from_pos) {

// Sample handling.
if (stream_playback->get_is_sample() && stream_playback->get_sample_playback().is_valid()) {
if (internal->scheduled_time > 0) {
WARN_PRINT_ED("play_scheduled() does not support samples. Playing immediately.");
}

Ref<AudioSamplePlayback> sample_playback = stream_playback->get_sample_playback();
sample_playback->offset = p_from_pos;
sample_playback->bus = _get_actual_bus();
Expand All @@ -609,6 +613,16 @@ void AudioStreamPlayer3D::play(float p_from_pos) {
}
}

void AudioStreamPlayer3D::play(float p_from_pos) {
internal->scheduled_time = 0;
_play_internal(p_from_pos);
}

void AudioStreamPlayer3D::play_scheduled(double p_abs_time, double p_from_pos) {
internal->scheduled_time = p_abs_time;
_play_internal(p_from_pos);
}

void AudioStreamPlayer3D::seek(float p_seconds) {
internal->seek(p_seconds);
}
Expand Down Expand Up @@ -822,6 +836,7 @@ void AudioStreamPlayer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_pitch_scale"), &AudioStreamPlayer3D::get_pitch_scale);

ClassDB::bind_method(D_METHOD("play", "from_position"), &AudioStreamPlayer3D::play, DEFVAL(0.0));
ClassDB::bind_method(D_METHOD("play_scheduled", "absolute_time", "from_position"), &AudioStreamPlayer3D::play_scheduled, DEFVAL(0.0));
ClassDB::bind_method(D_METHOD("seek", "to_position"), &AudioStreamPlayer3D::seek);
ClassDB::bind_method(D_METHOD("stop"), &AudioStreamPlayer3D::stop);

Expand Down
3 changes: 3 additions & 0 deletions scene/3d/audio_stream_player_3d.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ class AudioStreamPlayer3D : public Node3D {
#endif // PHYSICS_3D_DISABLED
Vector<AudioFrame> _update_panning();

void _play_internal(double p_from_pos = 0.0);

uint32_t area_mask = 1;

AudioServer::PlaybackType playback_type = AudioServer::PlaybackType::PLAYBACK_TYPE_DEFAULT;
Expand Down Expand Up @@ -155,6 +157,7 @@ class AudioStreamPlayer3D : public Node3D {
float get_pitch_scale() const;

void play(float p_from_pos = 0.0);
void play_scheduled(double p_abs_time, double p_from_pos = 0.0);
void seek(float p_seconds);
void stop();
bool is_playing() const;
Expand Down
19 changes: 17 additions & 2 deletions scene/audio/audio_stream_player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,20 @@ int AudioStreamPlayer::get_max_polyphony() const {
return internal->max_polyphony;
}

void AudioStreamPlayer::play(float p_from_pos) {
void AudioStreamPlayer::_play_internal(double p_from_pos) {
Ref<AudioStreamPlayback> stream_playback = internal->play_basic();
if (stream_playback.is_null()) {
return;
}
AudioServer::get_singleton()->start_playback_stream(stream_playback, internal->bus, _get_volume_vector(), p_from_pos, internal->pitch_scale);
AudioServer::get_singleton()->start_playback_stream(stream_playback, internal->bus, _get_volume_vector(), p_from_pos, internal->scheduled_time, internal->pitch_scale);
internal->ensure_playback_limit();

// Sample handling.
if (stream_playback->get_is_sample() && stream_playback->get_sample_playback().is_valid()) {
if (internal->scheduled_time > 0) {
WARN_PRINT_ED("play_scheduled() does not support samples. Playing immediately.");
}

Ref<AudioSamplePlayback> sample_playback = stream_playback->get_sample_playback();
sample_playback->offset = p_from_pos;
sample_playback->volume_vector = _get_volume_vector();
Expand All @@ -122,6 +126,16 @@ void AudioStreamPlayer::play(float p_from_pos) {
}
}

void AudioStreamPlayer::play(float p_from_pos) {
internal->scheduled_time = 0;
_play_internal(p_from_pos);
}

void AudioStreamPlayer::play_scheduled(double p_abs_time, double p_from_pos) {
internal->scheduled_time = p_abs_time;
_play_internal(p_from_pos);
}

void AudioStreamPlayer::seek(float p_seconds) {
internal->seek(p_seconds);
}
Expand Down Expand Up @@ -248,6 +262,7 @@ void AudioStreamPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_pitch_scale"), &AudioStreamPlayer::get_pitch_scale);

ClassDB::bind_method(D_METHOD("play", "from_position"), &AudioStreamPlayer::play, DEFVAL(0.0));
ClassDB::bind_method(D_METHOD("play_scheduled", "absolute_time", "from_position"), &AudioStreamPlayer::play_scheduled, DEFVAL(0.0));
ClassDB::bind_method(D_METHOD("seek", "to_position"), &AudioStreamPlayer::seek);
ClassDB::bind_method(D_METHOD("stop"), &AudioStreamPlayer::stop);

Expand Down
3 changes: 3 additions & 0 deletions scene/audio/audio_stream_player.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ class AudioStreamPlayer : public Node {

Vector<AudioFrame> _get_volume_vector();

void _play_internal(double p_from_pos = 0.0);

protected:
void _validate_property(PropertyInfo &p_property) const;
void _notification(int p_what);
Expand Down Expand Up @@ -89,6 +91,7 @@ class AudioStreamPlayer : public Node {
int get_max_polyphony() const;

void play(float p_from_pos = 0.0);
void play_scheduled(double p_abs_time, double p_from_pos = 0.0);
void seek(float p_seconds);
void stop();
bool is_playing() const;
Expand Down
4 changes: 2 additions & 2 deletions scene/audio/audio_stream_player_internal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ void AudioStreamPlayerInternal::set_stream(Ref<AudioStream> 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);
Expand All @@ -286,7 +286,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]);
Expand Down
5 changes: 3 additions & 2 deletions scene/audio/audio_stream_player_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class AudioStreamPlayerInternal : public Object {
bool autoplay = false;
StringName bus;
int max_polyphony = 1;
double scheduled_time = 0;

void process();
void ensure_playback_limit();
Expand All @@ -93,10 +94,10 @@ class AudioStreamPlayerInternal : public Object {
StringName get_bus() const;

Ref<AudioStreamPlayback> play_basic();
void seek(float p_seconds);
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;
Expand Down
Loading