Browse Source

Merge pull request #87061 from KoBeWi/shadow_of_the_former_self

Add AudioStreamPlayerInternal to unify stream players
Rémi Verschelde 1 year ago
parent
commit
8f9c815ddb

+ 50 - 212
scene/2d/audio_stream_player_2d.cpp

@@ -33,86 +33,48 @@
 #include "core/config/project_settings.h"
 #include "scene/2d/area_2d.h"
 #include "scene/2d/audio_listener_2d.h"
-#include "scene/main/window.h"
+#include "scene/audio/audio_stream_player_internal.h"
+#include "scene/main/viewport.h"
 #include "scene/resources/world_2d.h"
-
-#define PARAM_PREFIX "parameters/"
+#include "scene/scene_string_names.h"
+#include "servers/audio/audio_stream.h"
+#include "servers/audio_server.h"
 
 void AudioStreamPlayer2D::_notification(int p_what) {
+	internal->notification(p_what);
+
 	switch (p_what) {
 		case NOTIFICATION_ENTER_TREE: {
 			AudioServer::get_singleton()->add_listener_changed_callback(_listener_changed_cb, this);
-			if (autoplay && !Engine::get_singleton()->is_editor_hint()) {
-				play();
-			}
-			set_stream_paused(!can_process());
 		} break;
 
 		case NOTIFICATION_EXIT_TREE: {
-			set_stream_paused(true);
 			AudioServer::get_singleton()->remove_listener_changed_callback(_listener_changed_cb, this);
 		} break;
 
-		case NOTIFICATION_PREDELETE: {
-			stop();
-		} break;
-
-		case NOTIFICATION_PAUSED: {
-			if (!can_process()) {
-				// Node can't process so we start fading out to silence.
-				set_stream_paused(true);
-			}
-		} break;
-
-		case NOTIFICATION_UNPAUSED: {
-			set_stream_paused(false);
-		} break;
-
 		case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
 			// Update anything related to position first, if possible of course.
-			if (setplay.get() > 0 || (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count()) || force_update_panning) {
+			if (setplay.get() > 0 || (internal->active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count()) || force_update_panning) {
 				force_update_panning = false;
 				_update_panning();
 			}
 
 			if (setplayback.is_valid() && setplay.get() >= 0) {
-				active.set();
-				AudioServer::get_singleton()->start_playback_stream(setplayback, _get_actual_bus(), volume_vector, setplay.get(), pitch_scale);
+				internal->active.set();
+				AudioServer::get_singleton()->start_playback_stream(setplayback, _get_actual_bus(), volume_vector, setplay.get(), internal->pitch_scale);
 				setplayback.unref();
 				setplay.set(-1);
 			}
 
-			if (!stream_playbacks.is_empty() && active.is_set()) {
-				// Stop playing if no longer active.
-				Vector<Ref<AudioStreamPlayback>> playbacks_to_remove;
-				for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
-					if (playback.is_valid() && !AudioServer::get_singleton()->is_playback_active(playback) && !AudioServer::get_singleton()->is_playback_paused(playback)) {
-						playbacks_to_remove.push_back(playback);
-					}
-				}
-				// Now go through and remove playbacks that have finished. Removing elements from a Vector in a range based for is asking for trouble.
-				for (Ref<AudioStreamPlayback> &playback : playbacks_to_remove) {
-					stream_playbacks.erase(playback);
-				}
-				if (!playbacks_to_remove.is_empty() && stream_playbacks.is_empty()) {
-					// This node is no longer actively playing audio.
-					active.clear();
-					set_physics_process_internal(false);
-				}
-				if (!playbacks_to_remove.is_empty()) {
-					emit_signal(SNAME("finished"));
-				}
-			}
-
-			while (stream_playbacks.size() > max_polyphony) {
-				AudioServer::get_singleton()->stop_playback_stream(stream_playbacks[0]);
-				stream_playbacks.remove_at(0);
+			if (!internal->stream_playbacks.is_empty() && internal->active.is_set()) {
+				internal->process();
 			}
+			internal->ensure_playback_limit();
 		} break;
 	}
 }
 
-// Interacts with PhysicsServer2D, so can only be called during _physics_process
+// Interacts with PhysicsServer2D, so can only be called during _physics_process.
 StringName AudioStreamPlayer2D::_get_actual_bus() {
 	Vector2 global_pos = get_global_position();
 
@@ -144,12 +106,12 @@ StringName AudioStreamPlayer2D::_get_actual_bus() {
 
 		return area2d->get_audio_bus_name();
 	}
-	return default_bus;
+	return internal->bus;
 }
 
 // Interacts with PhysicsServer2D, so can only be called during _physics_process
 void AudioStreamPlayer2D::_update_panning() {
-	if (!active.is_set() || stream.is_null()) {
+	if (!internal->active.is_set() || internal->stream.is_null()) {
 		return;
 	}
 
@@ -194,7 +156,7 @@ void AudioStreamPlayer2D::_update_panning() {
 		}
 
 		float multiplier = Math::pow(1.0f - dist / max_distance, attenuation);
-		multiplier *= Math::db_to_linear(volume_db); // Also apply player volume!
+		multiplier *= Math::db_to_linear(internal->volume_db); // Also apply player volume!
 
 		float pan = relative_to_listener.x / screen_size.x;
 		// Don't let the panning effect extend (too far) beyond the screen.
@@ -213,178 +175,96 @@ void AudioStreamPlayer2D::_update_panning() {
 		volume_vector.write[0] = AudioFrame(MAX(prev_sample[0], new_sample[0]), MAX(prev_sample[1], new_sample[1]));
 	}
 
-	for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+	for (const Ref<AudioStreamPlayback> &playback : internal->stream_playbacks) {
 		AudioServer::get_singleton()->set_playback_bus_exclusive(playback, _get_actual_bus(), volume_vector);
 	}
 
-	for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
-		AudioServer::get_singleton()->set_playback_pitch_scale(playback, pitch_scale);
+	for (Ref<AudioStreamPlayback> &playback : internal->stream_playbacks) {
+		AudioServer::get_singleton()->set_playback_pitch_scale(playback, internal->pitch_scale);
 	}
 
 	last_mix_count = AudioServer::get_singleton()->get_mix_count();
 }
 
-void AudioStreamPlayer2D::_update_stream_parameters() {
-	if (stream.is_null()) {
-		return;
-	}
-
-	List<AudioStream::Parameter> parameters;
-	stream->get_parameter_list(&parameters);
-	for (const AudioStream::Parameter &K : parameters) {
-		const PropertyInfo &pi = K.property;
-		StringName key = PARAM_PREFIX + pi.name;
-		if (!playback_parameters.has(key)) {
-			ParameterData pd;
-			pd.path = pi.name;
-			pd.value = K.default_value;
-			playback_parameters.insert(key, pd);
-		}
-	}
-}
-
 void AudioStreamPlayer2D::set_stream(Ref<AudioStream> p_stream) {
-	if (stream.is_valid()) {
-		stream->disconnect(SNAME("parameter_list_changed"), callable_mp(this, &AudioStreamPlayer2D::_update_stream_parameters));
-	}
-	stop();
-	stream = p_stream;
-	_update_stream_parameters();
-	if (stream.is_valid()) {
-		stream->connect(SNAME("parameter_list_changed"), callable_mp(this, &AudioStreamPlayer2D::_update_stream_parameters));
-	}
-	notify_property_list_changed();
+	internal->set_stream(p_stream);
 }
 
 Ref<AudioStream> AudioStreamPlayer2D::get_stream() const {
-	return stream;
+	return internal->stream;
 }
 
 void AudioStreamPlayer2D::set_volume_db(float p_volume) {
-	volume_db = p_volume;
+	internal->volume_db = p_volume;
 }
 
 float AudioStreamPlayer2D::get_volume_db() const {
-	return volume_db;
+	return internal->volume_db;
 }
 
 void AudioStreamPlayer2D::set_pitch_scale(float p_pitch_scale) {
-	ERR_FAIL_COND(!(p_pitch_scale > 0.0));
-	pitch_scale = p_pitch_scale;
-	for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
-		AudioServer::get_singleton()->set_playback_pitch_scale(playback, p_pitch_scale);
-	}
+	internal->set_pitch_scale(p_pitch_scale);
 }
 
 float AudioStreamPlayer2D::get_pitch_scale() const {
-	return pitch_scale;
+	return internal->pitch_scale;
 }
 
 void AudioStreamPlayer2D::play(float p_from_pos) {
-	if (stream.is_null()) {
+	Ref<AudioStreamPlayback> stream_playback = internal->play_basic();
+	if (stream_playback.is_null()) {
 		return;
 	}
-	ERR_FAIL_COND_MSG(!is_inside_tree(), "Playback can only happen when a node is inside the scene tree");
-	if (stream->is_monophonic() && is_playing()) {
-		stop();
-	}
-	Ref<AudioStreamPlayback> stream_playback = stream->instantiate_playback();
-	ERR_FAIL_COND_MSG(stream_playback.is_null(), "Failed to instantiate playback.");
-
-	for (const KeyValue<StringName, ParameterData> &K : playback_parameters) {
-		stream_playback->set_parameter(K.value.path, K.value.value);
-	}
-
-	stream_playbacks.push_back(stream_playback);
 	setplayback = stream_playback;
 	setplay.set(p_from_pos);
-	active.set();
-	set_physics_process_internal(true);
 }
 
 void AudioStreamPlayer2D::seek(float p_seconds) {
-	if (is_playing()) {
-		stop();
-		play(p_seconds);
-	}
+	internal->seek(p_seconds);
 }
 
 void AudioStreamPlayer2D::stop() {
 	setplay.set(-1);
-	for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
-		AudioServer::get_singleton()->stop_playback_stream(playback);
-	}
-	stream_playbacks.clear();
-	active.clear();
-	set_physics_process_internal(false);
+	internal->stop();
 }
 
 bool AudioStreamPlayer2D::is_playing() const {
-	for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {
-		if (AudioServer::get_singleton()->is_playback_active(playback)) {
-			return true;
-		}
-	}
 	if (setplay.get() >= 0) {
 		return true; // play() has been called this frame, but no playback exists just yet.
 	}
-	return false;
+	return internal->is_playing();
 }
 
 float AudioStreamPlayer2D::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]);
-	}
-	return 0;
+	return internal->get_playback_position();
 }
 
 void AudioStreamPlayer2D::set_bus(const StringName &p_bus) {
-	default_bus = p_bus; // This will be pushed to the audio server during the next physics timestep, which is fast enough.
+	internal->bus = p_bus; // This will be pushed to the audio server during the next physics timestep, which is fast enough.
 }
 
 StringName AudioStreamPlayer2D::get_bus() const {
-	for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) {
-		if (AudioServer::get_singleton()->get_bus_name(i) == default_bus) {
-			return default_bus;
-		}
-	}
-	return SceneStringNames::get_singleton()->Master;
+	return internal->get_bus();
 }
 
 void AudioStreamPlayer2D::set_autoplay(bool p_enable) {
-	autoplay = p_enable;
+	internal->autoplay = p_enable;
 }
 
 bool AudioStreamPlayer2D::is_autoplay_enabled() {
-	return autoplay;
+	return internal->autoplay;
 }
 
 void AudioStreamPlayer2D::_set_playing(bool p_enable) {
-	if (p_enable) {
-		play();
-	} else {
-		stop();
-	}
+	internal->set_playing(p_enable);
 }
 
 bool AudioStreamPlayer2D::_is_active() const {
-	return active.is_set();
+	return internal->is_active();
 }
 
 void AudioStreamPlayer2D::_validate_property(PropertyInfo &p_property) const {
-	if (p_property.name == "bus") {
-		String options;
-		for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) {
-			if (i > 0) {
-				options += ",";
-			}
-			String name = AudioServer::get_singleton()->get_bus_name(i);
-			options += name;
-		}
-
-		p_property.hint_string = options;
-	}
+	internal->validate_property(p_property);
 }
 
 void AudioStreamPlayer2D::set_max_distance(float p_pixels) {
@@ -413,37 +293,27 @@ uint32_t AudioStreamPlayer2D::get_area_mask() const {
 }
 
 void AudioStreamPlayer2D::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<AudioStreamPlayback> &playback : stream_playbacks) {
-		AudioServer::get_singleton()->set_playback_paused(playback, p_pause);
-	}
+	internal->set_stream_paused(p_pause);
 }
 
 bool AudioStreamPlayer2D::get_stream_paused() const {
-	// There's currently no way to pause some playback streams but not others. Check the first and don't bother looking at the rest.
-	if (!stream_playbacks.is_empty()) {
-		return AudioServer::get_singleton()->is_playback_paused(stream_playbacks[0]);
-	}
-	return false;
+	return internal->get_stream_paused();
 }
 
 bool AudioStreamPlayer2D::has_stream_playback() {
-	return !stream_playbacks.is_empty();
+	return internal->has_stream_playback();
 }
 
 Ref<AudioStreamPlayback> AudioStreamPlayer2D::get_stream_playback() {
-	ERR_FAIL_COND_V_MSG(stream_playbacks.is_empty(), Ref<AudioStreamPlayback>(), "Player is inactive. Call play() before requesting get_stream_playback().");
-	return stream_playbacks[stream_playbacks.size() - 1];
+	return internal->get_stream_playback();
 }
 
 void AudioStreamPlayer2D::set_max_polyphony(int p_max_polyphony) {
-	if (p_max_polyphony > 0) {
-		max_polyphony = p_max_polyphony;
-	}
+	internal->set_max_polyphony(p_max_polyphony);
 }
 
 int AudioStreamPlayer2D::get_max_polyphony() const {
-	return max_polyphony;
+	return internal->max_polyphony;
 }
 
 void AudioStreamPlayer2D::set_panning_strength(float p_panning_strength) {
@@ -455,48 +325,16 @@ float AudioStreamPlayer2D::get_panning_strength() const {
 	return panning_strength;
 }
 
-void AudioStreamPlayer2D::_on_bus_layout_changed() {
-	notify_property_list_changed();
-}
-
-void AudioStreamPlayer2D::_on_bus_renamed(int p_bus_index, const StringName &p_old_name, const StringName &p_new_name) {
-	notify_property_list_changed();
-}
-
 bool AudioStreamPlayer2D::_set(const StringName &p_name, const Variant &p_value) {
-	HashMap<StringName, ParameterData>::Iterator I = playback_parameters.find(p_name);
-	if (!I) {
-		return false;
-	}
-	ParameterData &pd = I->value;
-	pd.value = p_value;
-	for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
-		playback->set_parameter(pd.path, pd.value);
-	}
-	return true;
+	return internal->set(p_name, p_value);
 }
 
 bool AudioStreamPlayer2D::_get(const StringName &p_name, Variant &r_ret) const {
-	HashMap<StringName, ParameterData>::ConstIterator I = playback_parameters.find(p_name);
-	if (!I) {
-		return false;
-	}
-
-	r_ret = I->value.value;
-	return true;
+	return internal->get(p_name, r_ret);
 }
 
 void AudioStreamPlayer2D::_get_property_list(List<PropertyInfo> *p_list) const {
-	if (stream.is_null()) {
-		return;
-	}
-	List<AudioStream::Parameter> parameters;
-	stream->get_parameter_list(&parameters);
-	for (const AudioStream::Parameter &K : parameters) {
-		PropertyInfo pi = K.property;
-		pi.name = PARAM_PREFIX + pi.name;
-		p_list->push_back(pi);
-	}
+	internal->get_property_list(p_list);
 }
 
 void AudioStreamPlayer2D::_bind_methods() {
@@ -563,11 +401,11 @@ void AudioStreamPlayer2D::_bind_methods() {
 }
 
 AudioStreamPlayer2D::AudioStreamPlayer2D() {
-	AudioServer::get_singleton()->connect("bus_layout_changed", callable_mp(this, &AudioStreamPlayer2D::_on_bus_layout_changed));
-	AudioServer::get_singleton()->connect("bus_renamed", callable_mp(this, &AudioStreamPlayer2D::_on_bus_renamed));
+	internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer2D::play), true));
 	cached_global_panning_strength = GLOBAL_GET("audio/general/2d_panning_strength");
 	set_hide_clip_children(true);
 }
 
 AudioStreamPlayer2D::~AudioStreamPlayer2D() {
+	memdelete(internal);
 }

+ 6 - 23
scene/2d/audio_stream_player_2d.h

@@ -32,9 +32,11 @@
 #define AUDIO_STREAM_PLAYER_2D_H
 
 #include "scene/2d/node_2d.h"
-#include "scene/scene_string_names.h"
-#include "servers/audio/audio_stream.h"
-#include "servers/audio_server.h"
+
+struct AudioFrame;
+class AudioStream;
+class AudioStreamPlayback;
+class AudioStreamPlayerInternal;
 
 class AudioStreamPlayer2D : public Node2D {
 	GDCLASS(AudioStreamPlayer2D, Node2D);
@@ -52,10 +54,8 @@ private:
 		Viewport *viewport = nullptr; //pointer only used for reference to previous mix
 	};
 
-	Vector<Ref<AudioStreamPlayback>> stream_playbacks;
-	Ref<AudioStream> stream;
+	AudioStreamPlayerInternal *internal = nullptr;
 
-	SafeFlag active{ false };
 	SafeNumeric<float> setplay{ -1.0 };
 	Ref<AudioStreamPlayback> setplayback;
 
@@ -64,21 +64,12 @@ private:
 	uint64_t last_mix_count = -1;
 	bool force_update_panning = false;
 
-	float volume_db = 0.0;
-	float pitch_scale = 1.0;
-	bool autoplay = false;
-	StringName default_bus = SceneStringNames::get_singleton()->Master;
-	int max_polyphony = 1;
-
 	void _set_playing(bool p_enable);
 	bool _is_active() const;
 
 	StringName _get_actual_bus();
 	void _update_panning();
 
-	void _on_bus_layout_changed();
-	void _on_bus_renamed(int p_bus_index, const StringName &p_old_name, const StringName &p_new_name);
-
 	static void _listener_changed_cb(void *self) { reinterpret_cast<AudioStreamPlayer2D *>(self)->force_update_panning = true; }
 
 	uint32_t area_mask = 1;
@@ -89,14 +80,6 @@ private:
 	float panning_strength = 1.0f;
 	float cached_global_panning_strength = 0.5f;
 
-	struct ParameterData {
-		StringName path;
-		Variant value;
-	};
-
-	HashMap<StringName, ParameterData> playback_parameters;
-	void _update_stream_parameters();
-
 protected:
 	void _validate_property(PropertyInfo &p_property) const;
 	void _notification(int p_what);

+ 49 - 211
scene/3d/audio_stream_player_3d.cpp

@@ -34,10 +34,10 @@
 #include "scene/3d/area_3d.h"
 #include "scene/3d/audio_listener_3d.h"
 #include "scene/3d/camera_3d.h"
+#include "scene/3d/velocity_tracker_3d.h"
+#include "scene/audio/audio_stream_player_internal.h"
 #include "scene/main/viewport.h"
-#include "scene/scene_string_names.h"
-
-#define PARAM_PREFIX "parameters/"
+#include "servers/audio/audio_stream.h"
 
 // Based on "A Novel Multichannel Panning Method for Standard and Arbitrary Loudspeaker Configurations" by Ramy Sadek and Chris Kyriakakis (2004)
 // Speaker-Placement Correction Amplitude Panning (SPCAP)
@@ -231,7 +231,7 @@ float AudioStreamPlayer3D::_get_attenuation_db(float p_distance) const {
 		}
 	}
 
-	att += volume_db;
+	att += internal->volume_db;
 	if (att > max_db) {
 		att = max_db;
 	}
@@ -244,32 +244,12 @@ void AudioStreamPlayer3D::_notification(int p_what) {
 		case NOTIFICATION_ENTER_TREE: {
 			velocity_tracker->reset(get_global_transform().origin);
 			AudioServer::get_singleton()->add_listener_changed_callback(_listener_changed_cb, this);
-			if (autoplay && !Engine::get_singleton()->is_editor_hint()) {
-				play();
-			}
-			set_stream_paused(!can_process());
 		} break;
 
 		case NOTIFICATION_EXIT_TREE: {
-			set_stream_paused(true);
 			AudioServer::get_singleton()->remove_listener_changed_callback(_listener_changed_cb, this);
 		} break;
 
-		case NOTIFICATION_PREDELETE: {
-			stop();
-		} break;
-
-		case NOTIFICATION_PAUSED: {
-			if (!can_process()) {
-				// Node can't process so we start fading out to silence.
-				set_stream_paused(true);
-			}
-		} break;
-
-		case NOTIFICATION_UNPAUSED: {
-			set_stream_paused(false);
-		} break;
-
 		case NOTIFICATION_TRANSFORM_CHANGED: {
 			if (doppler_tracking != DOPPLER_TRACKING_DISABLED) {
 				velocity_tracker->update_position(get_global_transform().origin);
@@ -279,13 +259,13 @@ void AudioStreamPlayer3D::_notification(int p_what) {
 		case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
 			// Update anything related to position first, if possible of course.
 			Vector<AudioFrame> volume_vector;
-			if (setplay.get() > 0 || (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count()) || force_update_panning) {
+			if (setplay.get() > 0 || (internal->active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count()) || force_update_panning) {
 				force_update_panning = false;
 				volume_vector = _update_panning();
 			}
 
 			if (setplayback.is_valid() && setplay.get() >= 0) {
-				active.set();
+				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);
@@ -293,32 +273,10 @@ void AudioStreamPlayer3D::_notification(int p_what) {
 				setplay.set(-1);
 			}
 
-			if (!stream_playbacks.is_empty() && active.is_set()) {
-				// Stop playing if no longer active.
-				Vector<Ref<AudioStreamPlayback>> playbacks_to_remove;
-				for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
-					if (playback.is_valid() && !AudioServer::get_singleton()->is_playback_active(playback) && !AudioServer::get_singleton()->is_playback_paused(playback)) {
-						playbacks_to_remove.push_back(playback);
-					}
-				}
-				// Now go through and remove playbacks that have finished. Removing elements from a Vector in a range based for is asking for trouble.
-				for (Ref<AudioStreamPlayback> &playback : playbacks_to_remove) {
-					stream_playbacks.erase(playback);
-				}
-				if (!playbacks_to_remove.is_empty() && stream_playbacks.is_empty()) {
-					// This node is no longer actively playing audio.
-					active.clear();
-					set_physics_process_internal(false);
-				}
-				if (!playbacks_to_remove.is_empty()) {
-					emit_signal(SNAME("finished"));
-				}
-			}
-
-			while (stream_playbacks.size() > max_polyphony) {
-				AudioServer::get_singleton()->stop_playback_stream(stream_playbacks[0]);
-				stream_playbacks.remove_at(0);
+			if (!internal->stream_playbacks.is_empty() && internal->active.is_set()) {
+				internal->process();
 			}
+			internal->ensure_playback_limit();
 		} break;
 	}
 }
@@ -362,16 +320,16 @@ Area3D *AudioStreamPlayer3D::_get_overriding_area() {
 	return nullptr;
 }
 
-// Interacts with PhysicsServer3D, so can only be called during _physics_process
+// Interacts with PhysicsServer3D, so can only be called during _physics_process.
 StringName AudioStreamPlayer3D::_get_actual_bus() {
 	Area3D *overriding_area = _get_overriding_area();
 	if (overriding_area && overriding_area->is_overriding_audio_bus() && !overriding_area->is_using_reverb_bus()) {
 		return overriding_area->get_audio_bus_name();
 	}
-	return bus;
+	return internal->bus;
 }
 
-// Interacts with PhysicsServer3D, so can only be called during _physics_process
+// Interacts with PhysicsServer3D, so can only be called during _physics_process.
 Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
 	Vector<AudioFrame> output_volume_vector;
 	output_volume_vector.resize(4);
@@ -379,7 +337,7 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
 		frame = AudioFrame(0, 0);
 	}
 
-	if (!active.is_set() || stream.is_null()) {
+	if (!internal->active.is_set() || internal->stream.is_null()) {
 		return output_volume_vector;
 	}
 
@@ -463,7 +421,7 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
 		}
 
 		linear_attenuation = Math::db_to_linear(db_att);
-		for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+		for (Ref<AudioStreamPlayback> &playback : internal->stream_playbacks) {
 			AudioServer::get_singleton()->set_playback_highshelf_params(playback, linear_attenuation, attenuation_filter_cutoff_hz);
 		}
 		// Bake in a constant factor here to allow the project setting defaults for 2d and 3d to be normalized to 1.0.
@@ -489,10 +447,10 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
 				bus_volumes[reverb_bus_name] = reverb_vol;
 			}
 		} else {
-			bus_volumes[bus] = output_volume_vector;
+			bus_volumes[internal->bus] = output_volume_vector;
 		}
 
-		for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+		for (Ref<AudioStreamPlayback> &playback : internal->stream_playbacks) {
 			AudioServer::get_singleton()->set_playback_bus_volumes_linear(playback, bus_volumes);
 		}
 
@@ -510,64 +468,37 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
 				float velocity = local_velocity.length();
 				float speed_of_sound = 343.0;
 
-				float doppler_pitch_scale = pitch_scale * speed_of_sound / (speed_of_sound + velocity * approaching);
+				float doppler_pitch_scale = internal->pitch_scale * speed_of_sound / (speed_of_sound + velocity * approaching);
 				doppler_pitch_scale = CLAMP(doppler_pitch_scale, (1 / 8.0), 8.0); //avoid crazy stuff
 
 				actual_pitch_scale = doppler_pitch_scale;
 			} else {
-				actual_pitch_scale = pitch_scale;
+				actual_pitch_scale = internal->pitch_scale;
 			}
 		} else {
-			actual_pitch_scale = pitch_scale;
+			actual_pitch_scale = internal->pitch_scale;
 		}
-		for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+		for (Ref<AudioStreamPlayback> &playback : internal->stream_playbacks) {
 			AudioServer::get_singleton()->set_playback_pitch_scale(playback, actual_pitch_scale);
 		}
 	}
 	return output_volume_vector;
 }
 
-void AudioStreamPlayer3D::_update_stream_parameters() {
-	if (stream.is_null()) {
-		return;
-	}
-	List<AudioStream::Parameter> parameters;
-	stream->get_parameter_list(&parameters);
-	for (const AudioStream::Parameter &K : parameters) {
-		const PropertyInfo &pi = K.property;
-		StringName key = PARAM_PREFIX + pi.name;
-		if (!playback_parameters.has(key)) {
-			ParameterData pd;
-			pd.path = pi.name;
-			pd.value = K.default_value;
-			playback_parameters.insert(key, pd);
-		}
-	}
-}
-
 void AudioStreamPlayer3D::set_stream(Ref<AudioStream> p_stream) {
-	if (stream.is_valid()) {
-		stream->disconnect(SNAME("parameter_list_changed"), callable_mp(this, &AudioStreamPlayer3D::_update_stream_parameters));
-	}
-	stop();
-	stream = p_stream;
-	_update_stream_parameters();
-	if (stream.is_valid()) {
-		stream->connect(SNAME("parameter_list_changed"), callable_mp(this, &AudioStreamPlayer3D::_update_stream_parameters));
-	}
-	notify_property_list_changed();
+	internal->set_stream(p_stream);
 }
 
 Ref<AudioStream> AudioStreamPlayer3D::get_stream() const {
-	return stream;
+	return internal->stream;
 }
 
 void AudioStreamPlayer3D::set_volume_db(float p_volume) {
-	volume_db = p_volume;
+	internal->volume_db = p_volume;
 }
 
 float AudioStreamPlayer3D::get_volume_db() const {
-	return volume_db;
+	return internal->volume_db;
 }
 
 void AudioStreamPlayer3D::set_unit_size(float p_volume) {
@@ -588,34 +519,20 @@ float AudioStreamPlayer3D::get_max_db() const {
 }
 
 void AudioStreamPlayer3D::set_pitch_scale(float p_pitch_scale) {
-	ERR_FAIL_COND(!(p_pitch_scale > 0.0));
-	pitch_scale = p_pitch_scale;
+	internal->set_pitch_scale(p_pitch_scale);
 }
 
 float AudioStreamPlayer3D::get_pitch_scale() const {
-	return pitch_scale;
+	return internal->pitch_scale;
 }
 
 void AudioStreamPlayer3D::play(float p_from_pos) {
-	if (stream.is_null()) {
+	Ref<AudioStreamPlayback> stream_playback = internal->play_basic();
+	if (stream_playback.is_null()) {
 		return;
 	}
-	ERR_FAIL_COND_MSG(!is_inside_tree(), "Playback can only happen when a node is inside the scene tree");
-	if (stream->is_monophonic() && is_playing()) {
-		stop();
-	}
-	Ref<AudioStreamPlayback> stream_playback = stream->instantiate_playback();
-	ERR_FAIL_COND_MSG(stream_playback.is_null(), "Failed to instantiate playback.");
-
-	for (const KeyValue<StringName, ParameterData> &K : playback_parameters) {
-		stream_playback->set_parameter(K.value.path, K.value.value);
-	}
-
-	stream_playbacks.push_back(stream_playback);
 	setplayback = stream_playback;
 	setplay.set(p_from_pos);
-	active.set();
-	set_physics_process_internal(true);
 }
 
 void AudioStreamPlayer3D::seek(float p_seconds) {
@@ -627,83 +544,46 @@ void AudioStreamPlayer3D::seek(float p_seconds) {
 
 void AudioStreamPlayer3D::stop() {
 	setplay.set(-1);
-	for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
-		AudioServer::get_singleton()->stop_playback_stream(playback);
-	}
-	stream_playbacks.clear();
-	active.clear();
-	set_physics_process_internal(false);
+	internal->stop();
 }
 
 bool AudioStreamPlayer3D::is_playing() const {
-	for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {
-		if (AudioServer::get_singleton()->is_playback_active(playback)) {
-			return true;
-		}
-	}
 	if (setplay.get() >= 0) {
 		return true; // play() has been called this frame, but no playback exists just yet.
 	}
-	return false;
+	return internal->is_playing();
 }
 
 float AudioStreamPlayer3D::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]);
-	}
-	return 0;
+	return internal->get_playback_position();
 }
 
 void AudioStreamPlayer3D::set_bus(const StringName &p_bus) {
-	//if audio is active, must lock this
-	AudioServer::get_singleton()->lock();
-	bus = p_bus;
-	AudioServer::get_singleton()->unlock();
+	internal->bus = p_bus; // This will be pushed to the audio server during the next physics timestep, which is fast enough.
 }
 
 StringName AudioStreamPlayer3D::get_bus() const {
-	for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) {
-		if (AudioServer::get_singleton()->get_bus_name(i) == bus) {
-			return bus;
-		}
-	}
-	return SceneStringNames::get_singleton()->Master;
+	return internal->get_bus();
 }
 
 void AudioStreamPlayer3D::set_autoplay(bool p_enable) {
-	autoplay = p_enable;
+	internal->autoplay = p_enable;
 }
 
 bool AudioStreamPlayer3D::is_autoplay_enabled() {
-	return autoplay;
+	return internal->autoplay;
 }
 
 void AudioStreamPlayer3D::_set_playing(bool p_enable) {
-	if (p_enable) {
-		play();
-	} else {
-		stop();
-	}
+	internal->set_playing(p_enable);
 }
 
 bool AudioStreamPlayer3D::_is_active() const {
-	return active.is_set();
+	return internal->is_active();
 }
 
 void AudioStreamPlayer3D::_validate_property(PropertyInfo &p_property) const {
-	if (p_property.name == "bus") {
-		String options;
-		for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) {
-			if (i > 0) {
-				options += ",";
-			}
-			String name = AudioServer::get_singleton()->get_bus_name(i);
-			options += name;
-		}
-
-		p_property.hint_string = options;
-	}
+	internal->validate_property(p_property);
 }
 
 void AudioStreamPlayer3D::set_max_distance(float p_metres) {
@@ -800,37 +680,27 @@ AudioStreamPlayer3D::DopplerTracking AudioStreamPlayer3D::get_doppler_tracking()
 }
 
 void AudioStreamPlayer3D::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<AudioStreamPlayback> &playback : stream_playbacks) {
-		AudioServer::get_singleton()->set_playback_paused(playback, p_pause);
-	}
+	internal->set_stream_paused(p_pause);
 }
 
 bool AudioStreamPlayer3D::get_stream_paused() const {
-	// There's currently no way to pause some playback streams but not others. Check the first and don't bother looking at the rest.
-	if (!stream_playbacks.is_empty()) {
-		return AudioServer::get_singleton()->is_playback_paused(stream_playbacks[0]);
-	}
-	return false;
+	return internal->get_stream_paused();
 }
 
 bool AudioStreamPlayer3D::has_stream_playback() {
-	return !stream_playbacks.is_empty();
+	return internal->has_stream_playback();
 }
 
 Ref<AudioStreamPlayback> AudioStreamPlayer3D::get_stream_playback() {
-	ERR_FAIL_COND_V_MSG(stream_playbacks.is_empty(), Ref<AudioStreamPlayback>(), "Player is inactive. Call play() before requesting get_stream_playback().");
-	return stream_playbacks[stream_playbacks.size() - 1];
+	return internal->get_stream_playback();
 }
 
 void AudioStreamPlayer3D::set_max_polyphony(int p_max_polyphony) {
-	if (p_max_polyphony > 0) {
-		max_polyphony = p_max_polyphony;
-	}
+	internal->set_max_polyphony(p_max_polyphony);
 }
 
 int AudioStreamPlayer3D::get_max_polyphony() const {
-	return max_polyphony;
+	return internal->max_polyphony;
 }
 
 void AudioStreamPlayer3D::set_panning_strength(float p_panning_strength) {
@@ -842,48 +712,16 @@ float AudioStreamPlayer3D::get_panning_strength() const {
 	return panning_strength;
 }
 
-void AudioStreamPlayer3D::_on_bus_layout_changed() {
-	notify_property_list_changed();
-}
-
-void AudioStreamPlayer3D::_on_bus_renamed(int p_bus_index, const StringName &p_old_name, const StringName &p_new_name) {
-	notify_property_list_changed();
-}
-
 bool AudioStreamPlayer3D::_set(const StringName &p_name, const Variant &p_value) {
-	HashMap<StringName, ParameterData>::Iterator I = playback_parameters.find(p_name);
-	if (!I) {
-		return false;
-	}
-	ParameterData &pd = I->value;
-	pd.value = p_value;
-	for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
-		playback->set_parameter(pd.path, pd.value);
-	}
-	return true;
+	return internal->set(p_name, p_value);
 }
 
 bool AudioStreamPlayer3D::_get(const StringName &p_name, Variant &r_ret) const {
-	HashMap<StringName, ParameterData>::ConstIterator I = playback_parameters.find(p_name);
-	if (!I) {
-		return false;
-	}
-
-	r_ret = I->value.value;
-	return true;
+	return internal->get(p_name, r_ret);
 }
 
 void AudioStreamPlayer3D::_get_property_list(List<PropertyInfo> *p_list) const {
-	if (stream.is_null()) {
-		return;
-	}
-	List<AudioStream::Parameter> parameters;
-	stream->get_parameter_list(&parameters);
-	for (const AudioStream::Parameter &K : parameters) {
-		PropertyInfo pi = K.property;
-		pi.name = PARAM_PREFIX + pi.name;
-		p_list->push_back(pi);
-	}
+	internal->get_property_list(p_list);
 }
 
 void AudioStreamPlayer3D::_bind_methods() {
@@ -994,12 +832,12 @@ void AudioStreamPlayer3D::_bind_methods() {
 }
 
 AudioStreamPlayer3D::AudioStreamPlayer3D() {
+	internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer3D::play), true));
 	velocity_tracker.instantiate();
-	AudioServer::get_singleton()->connect("bus_layout_changed", callable_mp(this, &AudioStreamPlayer3D::_on_bus_layout_changed));
-	AudioServer::get_singleton()->connect("bus_renamed", callable_mp(this, &AudioStreamPlayer3D::_on_bus_renamed));
 	set_disable_scale(true);
 	cached_global_panning_strength = GLOBAL_GET("audio/general/3d_panning_strength");
 }
 
 AudioStreamPlayer3D::~AudioStreamPlayer3D() {
+	memdelete(internal);
 }

+ 8 - 25
scene/3d/audio_stream_player_3d.h

@@ -31,15 +31,16 @@
 #ifndef AUDIO_STREAM_PLAYER_3D_H
 #define AUDIO_STREAM_PLAYER_3D_H
 
-#include "core/os/mutex.h"
-#include "scene/3d/area_3d.h"
 #include "scene/3d/node_3d.h"
-#include "scene/3d/velocity_tracker_3d.h"
-#include "servers/audio/audio_filter_sw.h"
-#include "servers/audio/audio_stream.h"
-#include "servers/audio_server.h"
 
+class Area3D;
+struct AudioFrame;
+class AudioStream;
+class AudioStreamPlayback;
+class AudioStreamPlayerInternal;
 class Camera3D;
+class VelocityTracker3D;
+
 class AudioStreamPlayer3D : public Node3D {
 	GDCLASS(AudioStreamPlayer3D, Node3D);
 
@@ -64,23 +65,16 @@ private:
 
 	};
 
-	Vector<Ref<AudioStreamPlayback>> stream_playbacks;
-	Ref<AudioStream> stream;
+	AudioStreamPlayerInternal *internal = nullptr;
 
-	SafeFlag active{ false };
 	SafeNumeric<float> setplay{ -1.0 };
 	Ref<AudioStreamPlayback> setplayback;
 
 	AttenuationModel attenuation_model = ATTENUATION_INVERSE_DISTANCE;
-	float volume_db = 0.0;
 	float unit_size = 10.0;
 	float max_db = 3.0;
-	float pitch_scale = 1.0;
 	// Internally used to take doppler tracking into account.
 	float actual_pitch_scale = 1.0;
-	bool autoplay = false;
-	StringName bus = SNAME("Master");
-	int max_polyphony = 1;
 
 	uint64_t last_mix_count = -1;
 	bool force_update_panning = false;
@@ -97,9 +91,6 @@ private:
 	Area3D *_get_overriding_area();
 	Vector<AudioFrame> _update_panning();
 
-	void _on_bus_layout_changed();
-	void _on_bus_renamed(int p_bus_index, const StringName &p_old_name, const StringName &p_new_name);
-
 	uint32_t area_mask = 1;
 
 	bool emission_angle_enabled = false;
@@ -121,14 +112,6 @@ private:
 	float panning_strength = 1.0f;
 	float cached_global_panning_strength = 0.5f;
 
-	struct ParameterData {
-		StringName path;
-		Variant value;
-	};
-
-	HashMap<StringName, ParameterData> playback_parameters;
-	void _update_stream_parameters();
-
 protected:
 	void _validate_property(PropertyInfo &p_property) const;
 	void _notification(int p_what);

+ 38 - 223
scene/audio/audio_stream_player.cpp

@@ -30,253 +30,104 @@
 
 #include "audio_stream_player.h"
 
-#include "core/config/engine.h"
-#include "core/math/audio_frame.h"
-#include "servers/audio_server.h"
-
-#define PARAM_PREFIX "parameters/"
+#include "scene/audio/audio_stream_player_internal.h"
+#include "servers/audio/audio_stream.h"
 
 void AudioStreamPlayer::_notification(int p_what) {
-	switch (p_what) {
-		case NOTIFICATION_ENTER_TREE: {
-			if (autoplay && !Engine::get_singleton()->is_editor_hint()) {
-				play();
-			}
-			set_stream_paused(!can_process());
-		} break;
-
-		case NOTIFICATION_INTERNAL_PROCESS: {
-			Vector<Ref<AudioStreamPlayback>> playbacks_to_remove;
-			for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
-				if (playback.is_valid() && !AudioServer::get_singleton()->is_playback_active(playback) && !AudioServer::get_singleton()->is_playback_paused(playback)) {
-					playbacks_to_remove.push_back(playback);
-				}
-			}
-			// Now go through and remove playbacks that have finished. Removing elements from a Vector in a range based for is asking for trouble.
-			for (Ref<AudioStreamPlayback> &playback : playbacks_to_remove) {
-				stream_playbacks.erase(playback);
-			}
-			if (!playbacks_to_remove.is_empty() && stream_playbacks.is_empty()) {
-				// This node is no longer actively playing audio.
-				active.clear();
-				set_process_internal(false);
-			}
-			if (!playbacks_to_remove.is_empty()) {
-				emit_signal(SNAME("finished"));
-			}
-		} break;
-
-		case NOTIFICATION_EXIT_TREE: {
-			set_stream_paused(true);
-		} break;
-
-		case NOTIFICATION_PREDELETE: {
-			for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
-				AudioServer::get_singleton()->stop_playback_stream(playback);
-			}
-			stream_playbacks.clear();
-		} break;
-
-		case NOTIFICATION_PAUSED: {
-			if (!can_process()) {
-				// Node can't process so we start fading out to silence
-				set_stream_paused(true);
-			}
-		} break;
-
-		case NOTIFICATION_UNPAUSED: {
-			set_stream_paused(false);
-		} break;
-	}
-}
-
-void AudioStreamPlayer::_update_stream_parameters() {
-	if (stream.is_null()) {
-		return;
-	}
-	List<AudioStream::Parameter> parameters;
-	stream->get_parameter_list(&parameters);
-	for (const AudioStream::Parameter &K : parameters) {
-		const PropertyInfo &pi = K.property;
-		StringName key = PARAM_PREFIX + pi.name;
-		if (!playback_parameters.has(key)) {
-			ParameterData pd;
-			pd.path = pi.name;
-			pd.value = K.default_value;
-			playback_parameters.insert(key, pd);
-		}
-	}
+	internal->notification(p_what);
 }
 
 void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) {
-	if (stream.is_valid()) {
-		stream->disconnect(SNAME("parameter_list_changed"), callable_mp(this, &AudioStreamPlayer::_update_stream_parameters));
-	}
-	stop();
-	stream = p_stream;
-	_update_stream_parameters();
-	if (stream.is_valid()) {
-		stream->connect(SNAME("parameter_list_changed"), callable_mp(this, &AudioStreamPlayer::_update_stream_parameters));
-	}
-	notify_property_list_changed();
+	internal->set_stream(p_stream);
 }
 
 bool AudioStreamPlayer::_set(const StringName &p_name, const Variant &p_value) {
-	HashMap<StringName, ParameterData>::Iterator I = playback_parameters.find(p_name);
-	if (!I) {
-		return false;
-	}
-	ParameterData &pd = I->value;
-	pd.value = p_value;
-	for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
-		playback->set_parameter(pd.path, pd.value);
-	}
-	return true;
+	return internal->set(p_name, p_value);
 }
 
 bool AudioStreamPlayer::_get(const StringName &p_name, Variant &r_ret) const {
-	HashMap<StringName, ParameterData>::ConstIterator I = playback_parameters.find(p_name);
-	if (!I) {
-		return false;
-	}
-
-	r_ret = I->value.value;
-	return true;
+	return internal->get(p_name, r_ret);
 }
 
 void AudioStreamPlayer::_get_property_list(List<PropertyInfo> *p_list) const {
-	if (stream.is_null()) {
-		return;
-	}
-	List<AudioStream::Parameter> parameters;
-	stream->get_parameter_list(&parameters);
-	for (const AudioStream::Parameter &K : parameters) {
-		PropertyInfo pi = K.property;
-		pi.name = PARAM_PREFIX + pi.name;
-		p_list->push_back(pi);
-	}
+	internal->get_property_list(p_list);
 }
 
 Ref<AudioStream> AudioStreamPlayer::get_stream() const {
-	return stream;
+	return internal->stream;
 }
 
 void AudioStreamPlayer::set_volume_db(float p_volume) {
-	volume_db = p_volume;
+	internal->volume_db = p_volume;
 
 	Vector<AudioFrame> volume_vector = _get_volume_vector();
-	for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+	for (Ref<AudioStreamPlayback> &playback : internal->stream_playbacks) {
 		AudioServer::get_singleton()->set_playback_all_bus_volumes_linear(playback, volume_vector);
 	}
 }
 
 float AudioStreamPlayer::get_volume_db() const {
-	return volume_db;
+	return internal->volume_db;
 }
 
 void AudioStreamPlayer::set_pitch_scale(float p_pitch_scale) {
-	ERR_FAIL_COND(!(p_pitch_scale > 0.0));
-	pitch_scale = p_pitch_scale;
-
-	for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
-		AudioServer::get_singleton()->set_playback_pitch_scale(playback, pitch_scale);
-	}
+	internal->set_pitch_scale(p_pitch_scale);
 }
 
 float AudioStreamPlayer::get_pitch_scale() const {
-	return pitch_scale;
+	return internal->pitch_scale;
 }
 
 void AudioStreamPlayer::set_max_polyphony(int p_max_polyphony) {
-	if (p_max_polyphony > 0) {
-		max_polyphony = p_max_polyphony;
-	}
+	internal->set_max_polyphony(p_max_polyphony);
 }
 
 int AudioStreamPlayer::get_max_polyphony() const {
-	return max_polyphony;
+	return internal->max_polyphony;
 }
 
 void AudioStreamPlayer::play(float p_from_pos) {
-	if (stream.is_null()) {
+	Ref<AudioStreamPlayback> stream_playback = internal->play_basic();
+	if (stream_playback.is_null()) {
 		return;
 	}
-	ERR_FAIL_COND_MSG(!is_inside_tree(), "Playback can only happen when a node is inside the scene tree");
-	if (stream->is_monophonic() && is_playing()) {
-		stop();
-	}
-	Ref<AudioStreamPlayback> stream_playback = stream->instantiate_playback();
-	ERR_FAIL_COND_MSG(stream_playback.is_null(), "Failed to instantiate playback.");
-
-	for (const KeyValue<StringName, ParameterData> &K : playback_parameters) {
-		stream_playback->set_parameter(K.value.path, K.value.value);
-	}
-
-	AudioServer::get_singleton()->start_playback_stream(stream_playback, bus, _get_volume_vector(), p_from_pos, pitch_scale);
-	stream_playbacks.push_back(stream_playback);
-	active.set();
-	set_process_internal(true);
-	while (stream_playbacks.size() > max_polyphony) {
-		AudioServer::get_singleton()->stop_playback_stream(stream_playbacks[0]);
-		stream_playbacks.remove_at(0);
-	}
+	AudioServer::get_singleton()->start_playback_stream(stream_playback, internal->bus, _get_volume_vector(), p_from_pos, internal->pitch_scale);
+	internal->ensure_playback_limit();
 }
 
 void AudioStreamPlayer::seek(float p_seconds) {
-	if (is_playing()) {
-		stop();
-		play(p_seconds);
-	}
+	internal->seek(p_seconds);
 }
 
 void AudioStreamPlayer::stop() {
-	for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
-		AudioServer::get_singleton()->stop_playback_stream(playback);
-	}
-	stream_playbacks.clear();
-	active.clear();
-	set_process_internal(false);
+	internal->stop();
 }
 
 bool AudioStreamPlayer::is_playing() const {
-	for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {
-		if (AudioServer::get_singleton()->is_playback_active(playback)) {
-			return true;
-		}
-	}
-	return false;
+	return internal->is_playing();
 }
 
 float AudioStreamPlayer::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]);
-	}
-	return 0;
+	return internal->get_playback_position();
 }
 
 void AudioStreamPlayer::set_bus(const StringName &p_bus) {
-	bus = p_bus;
-	for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+	internal->bus = p_bus;
+	for (const Ref<AudioStreamPlayback> &playback : internal->stream_playbacks) {
 		AudioServer::get_singleton()->set_playback_bus_exclusive(playback, p_bus, _get_volume_vector());
 	}
 }
 
 StringName AudioStreamPlayer::get_bus() const {
-	for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) {
-		if (AudioServer::get_singleton()->get_bus_name(i) == String(bus)) {
-			return bus;
-		}
-	}
-	return SceneStringNames::get_singleton()->Master;
+	return internal->get_bus();
 }
 
 void AudioStreamPlayer::set_autoplay(bool p_enable) {
-	autoplay = p_enable;
+	internal->autoplay = p_enable;
 }
 
 bool AudioStreamPlayer::is_autoplay_enabled() {
-	return autoplay;
+	return internal->autoplay;
 }
 
 void AudioStreamPlayer::set_mix_target(MixTarget p_target) {
@@ -288,43 +139,19 @@ AudioStreamPlayer::MixTarget AudioStreamPlayer::get_mix_target() const {
 }
 
 void AudioStreamPlayer::_set_playing(bool p_enable) {
-	if (p_enable) {
-		play();
-	} else {
-		stop();
-	}
+	internal->set_playing(p_enable);
 }
 
 bool AudioStreamPlayer::_is_active() const {
-	for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {
-		if (AudioServer::get_singleton()->is_playback_active(playback)) {
-			return true;
-		}
-	}
-	return false;
-}
-
-void AudioStreamPlayer::_on_bus_layout_changed() {
-	notify_property_list_changed();
-}
-
-void AudioStreamPlayer::_on_bus_renamed(int p_bus_index, const StringName &p_old_name, const StringName &p_new_name) {
-	notify_property_list_changed();
+	return internal->is_active();
 }
 
 void AudioStreamPlayer::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<AudioStreamPlayback> &playback : stream_playbacks) {
-		AudioServer::get_singleton()->set_playback_paused(playback, p_pause);
-	}
+	internal->set_stream_paused(p_pause);
 }
 
 bool AudioStreamPlayer::get_stream_paused() const {
-	// There's currently no way to pause some playback streams but not others. Check the first and don't bother looking at the rest.
-	if (!stream_playbacks.is_empty()) {
-		return AudioServer::get_singleton()->is_playback_paused(stream_playbacks[0]);
-	}
-	return false;
+	return internal->get_stream_paused();
 }
 
 Vector<AudioFrame> AudioStreamPlayer::_get_volume_vector() {
@@ -337,7 +164,7 @@ Vector<AudioFrame> AudioStreamPlayer::_get_volume_vector() {
 		channel_volume_db = AudioFrame(0, 0);
 	}
 
-	float volume_linear = Math::db_to_linear(volume_db);
+	float volume_linear = Math::db_to_linear(internal->volume_db);
 
 	// Set the volume vector up according to the speaker mode and mix target.
 	// TODO do we need to scale the volume down when we output to more channels?
@@ -365,27 +192,15 @@ Vector<AudioFrame> AudioStreamPlayer::_get_volume_vector() {
 }
 
 void AudioStreamPlayer::_validate_property(PropertyInfo &p_property) const {
-	if (p_property.name == "bus") {
-		String options;
-		for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) {
-			if (i > 0) {
-				options += ",";
-			}
-			String name = AudioServer::get_singleton()->get_bus_name(i);
-			options += name;
-		}
-
-		p_property.hint_string = options;
-	}
+	internal->validate_property(p_property);
 }
 
 bool AudioStreamPlayer::has_stream_playback() {
-	return !stream_playbacks.is_empty();
+	return internal->has_stream_playback();
 }
 
 Ref<AudioStreamPlayback> AudioStreamPlayer::get_stream_playback() {
-	ERR_FAIL_COND_V_MSG(stream_playbacks.is_empty(), Ref<AudioStreamPlayback>(), "Player is inactive. Call play() before requesting get_stream_playback().");
-	return stream_playbacks[stream_playbacks.size() - 1];
+	return internal->get_stream_playback();
 }
 
 void AudioStreamPlayer::_bind_methods() {
@@ -444,9 +259,9 @@ void AudioStreamPlayer::_bind_methods() {
 }
 
 AudioStreamPlayer::AudioStreamPlayer() {
-	AudioServer::get_singleton()->connect("bus_layout_changed", callable_mp(this, &AudioStreamPlayer::_on_bus_layout_changed));
-	AudioServer::get_singleton()->connect("bus_renamed", callable_mp(this, &AudioStreamPlayer::_on_bus_renamed));
+	internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer::play), false));
 }
 
 AudioStreamPlayer::~AudioStreamPlayer() {
+	memdelete(internal);
 }

+ 6 - 24
scene/audio/audio_stream_player.h

@@ -31,10 +31,12 @@
 #ifndef AUDIO_STREAM_PLAYER_H
 #define AUDIO_STREAM_PLAYER_H
 
-#include "core/templates/safe_refcount.h"
 #include "scene/main/node.h"
-#include "scene/scene_string_names.h"
-#include "servers/audio/audio_stream.h"
+
+struct AudioFrame;
+class AudioStream;
+class AudioStreamPlayback;
+class AudioStreamPlayerInternal;
 
 class AudioStreamPlayer : public Node {
 	GDCLASS(AudioStreamPlayer, Node);
@@ -47,35 +49,15 @@ public:
 	};
 
 private:
-	Vector<Ref<AudioStreamPlayback>> stream_playbacks;
-	Ref<AudioStream> stream;
-
-	SafeFlag active;
-
-	float pitch_scale = 1.0;
-	float volume_db = 0.0;
-	bool autoplay = false;
-	StringName bus = SceneStringNames::get_singleton()->Master;
-	int max_polyphony = 1;
+	AudioStreamPlayerInternal *internal = nullptr;
 
 	MixTarget mix_target = MIX_TARGET_STEREO;
 
 	void _set_playing(bool p_enable);
 	bool _is_active() const;
 
-	void _on_bus_layout_changed();
-	void _on_bus_renamed(int p_bus_index, const StringName &p_old_name, const StringName &p_new_name);
-
 	Vector<AudioFrame> _get_volume_vector();
 
-	struct ParameterData {
-		StringName path;
-		Variant value;
-	};
-
-	HashMap<StringName, ParameterData> playback_parameters;
-	void _update_stream_parameters();
-
 protected:
 	void _validate_property(PropertyInfo &p_property) const;
 	void _notification(int p_what);

+ 321 - 0
scene/audio/audio_stream_player_internal.cpp

@@ -0,0 +1,321 @@
+/**************************************************************************/
+/*  audio_stream_player_internal.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_player_internal.h"
+
+#include "scene/main/node.h"
+#include "scene/scene_string_names.h"
+#include "servers/audio/audio_stream.h"
+
+void AudioStreamPlayerInternal::_set_process(bool p_enabled) {
+	if (physical) {
+		node->set_physics_process_internal(p_enabled);
+	} else {
+		node->set_process(p_enabled);
+	}
+}
+
+void AudioStreamPlayerInternal::_update_stream_parameters() {
+	if (stream.is_null()) {
+		return;
+	}
+
+	List<AudioStream::Parameter> parameters;
+	stream->get_parameter_list(&parameters);
+	for (const AudioStream::Parameter &K : parameters) {
+		const PropertyInfo &pi = K.property;
+		StringName key = PARAM_PREFIX + pi.name;
+		if (!playback_parameters.has(key)) {
+			ParameterData pd;
+			pd.path = pi.name;
+			pd.value = K.default_value;
+			playback_parameters.insert(key, pd);
+		}
+	}
+}
+
+void AudioStreamPlayerInternal::process() {
+	Vector<Ref<AudioStreamPlayback>> playbacks_to_remove;
+	for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+		if (playback.is_valid() && !AudioServer::get_singleton()->is_playback_active(playback) && !AudioServer::get_singleton()->is_playback_paused(playback)) {
+			playbacks_to_remove.push_back(playback);
+		}
+	}
+	// Now go through and remove playbacks that have finished. Removing elements from a Vector in a range based for is asking for trouble.
+	for (Ref<AudioStreamPlayback> &playback : playbacks_to_remove) {
+		stream_playbacks.erase(playback);
+	}
+	if (!playbacks_to_remove.is_empty() && stream_playbacks.is_empty()) {
+		// This node is no longer actively playing audio.
+		active.clear();
+		_set_process(false);
+	}
+	if (!playbacks_to_remove.is_empty()) {
+		node->emit_signal(SNAME("finished"));
+	}
+}
+
+void AudioStreamPlayerInternal::ensure_playback_limit() {
+	while (stream_playbacks.size() > max_polyphony) {
+		AudioServer::get_singleton()->stop_playback_stream(stream_playbacks[0]);
+		stream_playbacks.remove_at(0);
+	}
+}
+
+void AudioStreamPlayerInternal::notification(int p_what) {
+	switch (p_what) {
+		case Node::NOTIFICATION_ENTER_TREE: {
+			if (autoplay && !Engine::get_singleton()->is_editor_hint()) {
+				play_callable.call(0.0);
+			}
+			set_stream_paused(!node->can_process());
+		} break;
+
+		case Node::NOTIFICATION_EXIT_TREE: {
+			set_stream_paused(true);
+		} break;
+
+		case Node::NOTIFICATION_INTERNAL_PROCESS: {
+			process();
+		} break;
+
+		case Node::NOTIFICATION_PREDELETE: {
+			for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+				AudioServer::get_singleton()->stop_playback_stream(playback);
+			}
+			stream_playbacks.clear();
+		} break;
+
+		case Node::NOTIFICATION_PAUSED: {
+			if (!node->can_process()) {
+				// Node can't process so we start fading out to silence
+				set_stream_paused(true);
+			}
+		} break;
+
+		case Node::NOTIFICATION_UNPAUSED: {
+			set_stream_paused(false);
+		} break;
+	}
+}
+
+Ref<AudioStreamPlayback> AudioStreamPlayerInternal::play_basic() {
+	Ref<AudioStreamPlayback> stream_playback;
+	if (stream.is_null()) {
+		return stream_playback;
+	}
+	ERR_FAIL_COND_V_MSG(!node->is_inside_tree(), stream_playback, "Playback can only happen when a node is inside the scene tree");
+	if (stream->is_monophonic() && is_playing()) {
+		stop();
+	}
+	stream_playback = stream->instantiate_playback();
+	ERR_FAIL_COND_V_MSG(stream_playback.is_null(), stream_playback, "Failed to instantiate playback.");
+
+	for (const KeyValue<StringName, ParameterData> &K : playback_parameters) {
+		stream_playback->set_parameter(K.value.path, K.value.value);
+	}
+
+	stream_playbacks.push_back(stream_playback);
+	active.set();
+	_set_process(true);
+	return stream_playback;
+}
+
+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<AudioStreamPlayback> &playback : stream_playbacks) {
+		AudioServer::get_singleton()->set_playback_paused(playback, p_pause);
+	}
+}
+
+bool AudioStreamPlayerInternal::get_stream_paused() const {
+	// There's currently no way to pause some playback streams but not others. Check the first and don't bother looking at the rest.
+	if (!stream_playbacks.is_empty()) {
+		return AudioServer::get_singleton()->is_playback_paused(stream_playbacks[0]);
+	}
+	return false;
+}
+
+void AudioStreamPlayerInternal::validate_property(PropertyInfo &p_property) const {
+	if (p_property.name == "bus") {
+		String options;
+		for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) {
+			if (i > 0) {
+				options += ",";
+			}
+			String name = AudioServer::get_singleton()->get_bus_name(i);
+			options += name;
+		}
+
+		p_property.hint_string = options;
+	}
+}
+
+bool AudioStreamPlayerInternal::set(const StringName &p_name, const Variant &p_value) {
+	ParameterData *pd = playback_parameters.getptr(p_name);
+	if (!pd) {
+		return false;
+	}
+	pd->value = p_value;
+	for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+		playback->set_parameter(pd->path, pd->value);
+	}
+	return true;
+}
+
+bool AudioStreamPlayerInternal::get(const StringName &p_name, Variant &r_ret) const {
+	const ParameterData *pd = playback_parameters.getptr(p_name);
+	if (!pd) {
+		return false;
+	}
+	r_ret = pd->value;
+	return true;
+}
+
+void AudioStreamPlayerInternal::get_property_list(List<PropertyInfo> *p_list) const {
+	if (stream.is_null()) {
+		return;
+	}
+	List<AudioStream::Parameter> parameters;
+	stream->get_parameter_list(&parameters);
+	for (const AudioStream::Parameter &K : parameters) {
+		PropertyInfo pi = K.property;
+		pi.name = PARAM_PREFIX + pi.name;
+
+		const ParameterData *pd = playback_parameters.getptr(pi.name);
+		if (pd && pd->value == K.default_value) {
+			pi.usage &= ~PROPERTY_USAGE_STORAGE;
+		}
+
+		p_list->push_back(pi);
+	}
+}
+
+void AudioStreamPlayerInternal::set_stream(Ref<AudioStream> p_stream) {
+	if (stream.is_valid()) {
+		stream->disconnect(SNAME("parameter_list_changed"), callable_mp(this, &AudioStreamPlayerInternal::_update_stream_parameters));
+	}
+	stop();
+	stream = p_stream;
+	_update_stream_parameters();
+	if (stream.is_valid()) {
+		stream->connect(SNAME("parameter_list_changed"), callable_mp(this, &AudioStreamPlayerInternal::_update_stream_parameters));
+	}
+	node->notify_property_list_changed();
+}
+
+void AudioStreamPlayerInternal::seek(float p_seconds) {
+	if (is_playing()) {
+		stop();
+		play_callable.call(p_seconds);
+	}
+}
+
+void AudioStreamPlayerInternal::stop() {
+	for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+		AudioServer::get_singleton()->stop_playback_stream(playback);
+	}
+	stream_playbacks.clear();
+	active.clear();
+	_set_process(false);
+}
+
+bool AudioStreamPlayerInternal::is_playing() const {
+	for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+		if (AudioServer::get_singleton()->is_playback_active(playback)) {
+			return true;
+		}
+	}
+	return false;
+}
+
+float 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]);
+	}
+	return 0;
+}
+
+void AudioStreamPlayerInternal::set_playing(bool p_enable) {
+	if (p_enable) {
+		play_callable.call(0.0);
+	} else {
+		stop();
+	}
+}
+
+bool AudioStreamPlayerInternal::is_active() const {
+	return active.is_set();
+}
+
+void AudioStreamPlayerInternal::set_pitch_scale(float p_pitch_scale) {
+	ERR_FAIL_COND(p_pitch_scale <= 0.0);
+	pitch_scale = p_pitch_scale;
+
+	for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+		AudioServer::get_singleton()->set_playback_pitch_scale(playback, pitch_scale);
+	}
+}
+
+void AudioStreamPlayerInternal::set_max_polyphony(int p_max_polyphony) {
+	if (p_max_polyphony > 0) {
+		max_polyphony = p_max_polyphony;
+	}
+}
+
+bool AudioStreamPlayerInternal::has_stream_playback() {
+	return !stream_playbacks.is_empty();
+}
+
+Ref<AudioStreamPlayback> AudioStreamPlayerInternal::get_stream_playback() {
+	ERR_FAIL_COND_V_MSG(stream_playbacks.is_empty(), Ref<AudioStreamPlayback>(), "Player is inactive. Call play() before requesting get_stream_playback().");
+	return stream_playbacks[stream_playbacks.size() - 1];
+}
+
+StringName AudioStreamPlayerInternal::get_bus() const {
+	const String bus_name = bus;
+	for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) {
+		if (AudioServer::get_singleton()->get_bus_name(i) == bus_name) {
+			return bus;
+		}
+	}
+	return SceneStringNames::get_singleton()->Master;
+}
+
+AudioStreamPlayerInternal::AudioStreamPlayerInternal(Node *p_node, const Callable &p_play_callable, bool p_physical) {
+	node = p_node;
+	play_callable = p_play_callable;
+	physical = p_physical;
+	bus = SceneStringNames::get_singleton()->Master;
+
+	AudioServer::get_singleton()->connect("bus_layout_changed", callable_mp((Object *)node, &Object::notify_property_list_changed));
+	AudioServer::get_singleton()->connect("bus_renamed", callable_mp((Object *)node, &Object::notify_property_list_changed).unbind(3));
+}

+ 105 - 0
scene/audio/audio_stream_player_internal.h

@@ -0,0 +1,105 @@
+/**************************************************************************/
+/*  audio_stream_player_internal.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.                 */
+/**************************************************************************/
+
+#ifndef AUDIO_STREAM_PLAYER_INTERNAL_H
+#define AUDIO_STREAM_PLAYER_INTERNAL_H
+
+#include "core/object/ref_counted.h"
+#include "core/templates/safe_refcount.h"
+
+class AudioStream;
+class AudioStreamPlayback;
+class Node;
+
+class AudioStreamPlayerInternal : public Object {
+	GDCLASS(AudioStreamPlayerInternal, Object);
+
+	struct ParameterData {
+		StringName path;
+		Variant value;
+	};
+
+	static inline const String PARAM_PREFIX = "parameters/";
+
+	Node *node = nullptr;
+	Callable play_callable;
+	bool physical = false;
+
+	HashMap<StringName, ParameterData> playback_parameters;
+
+	void _set_process(bool p_enabled);
+	void _update_stream_parameters();
+
+public:
+	Vector<Ref<AudioStreamPlayback>> stream_playbacks;
+	Ref<AudioStream> stream;
+
+	SafeFlag active;
+
+	float pitch_scale = 1.0;
+	float volume_db = 0.0;
+	bool autoplay = false;
+	StringName bus;
+	int max_polyphony = 1;
+
+	void process();
+	void ensure_playback_limit();
+
+	void notification(int p_what);
+	void validate_property(PropertyInfo &p_property) const;
+	bool set(const StringName &p_name, const Variant &p_value);
+	bool get(const StringName &p_name, Variant &r_ret) const;
+	void get_property_list(List<PropertyInfo> *p_list) const;
+
+	void set_stream(Ref<AudioStream> p_stream);
+	void set_pitch_scale(float p_pitch_scale);
+	void set_max_polyphony(int p_max_polyphony);
+
+	StringName get_bus() const;
+
+	Ref<AudioStreamPlayback> play_basic();
+	void seek(float p_seconds);
+	void stop();
+	bool is_playing() const;
+	float get_playback_position();
+
+	void set_playing(bool p_enable);
+	bool is_active() const;
+
+	void set_stream_paused(bool p_pause);
+	bool get_stream_paused() const;
+
+	bool has_stream_playback();
+	Ref<AudioStreamPlayback> get_stream_playback();
+
+	AudioStreamPlayerInternal(Node *p_node, const Callable &p_play_callable, bool p_physical);
+};
+
+#endif // AUDIO_STREAM_PLAYER_INTERNAL_H