Browse Source

Merge pull request #52237 from ellenhp/polyphony

Add optional polyphonic playback to built-in audio player nodes
Juan Linietsky 4 years ago
parent
commit
ca11f8ad30

+ 11 - 0
doc/classes/AudioStream.xml

@@ -28,12 +28,23 @@
 			<description>
 			</description>
 		</method>
+		<method name="_is_monophonic" qualifiers="virtual const">
+			<return type="bool" />
+			<description>
+			</description>
+		</method>
 		<method name="get_length" qualifiers="const">
 			<return type="float" />
 			<description>
 				Returns the length of the audio stream in seconds.
 			</description>
 		</method>
+		<method name="is_monophonic" qualifiers="const">
+			<return type="bool" />
+			<description>
+				Returns true if this audio stream only supports monophonic playback, or false if the audio stream supports polyphony.
+			</description>
+		</method>
 	</methods>
 	<constants>
 	</constants>

+ 3 - 0
doc/classes/AudioStreamPlayer.xml

@@ -56,6 +56,9 @@
 		<member name="bus" type="StringName" setter="set_bus" getter="get_bus" default="&amp;&quot;Master&quot;">
 			Bus on which this audio is playing.
 		</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.
+		</member>
 		<member name="mix_target" type="int" setter="set_mix_target" getter="get_mix_target" enum="AudioStreamPlayer.MixTarget" default="0">
 			If the audio configuration has more than two speakers, this sets the target channels. See [enum MixTarget] constants.
 		</member>

+ 3 - 0
doc/classes/AudioStreamPlayer2D.xml

@@ -61,6 +61,9 @@
 		<member name="max_distance" type="float" setter="set_max_distance" getter="get_max_distance" default="2000.0">
 			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.
+		</member>
 		<member name="pitch_scale" type="float" setter="set_pitch_scale" getter="get_pitch_scale" default="1.0">
 			The pitch and the tempo of the audio, as a multiplier of the audio sample's sample rate.
 		</member>

+ 3 - 0
doc/classes/AudioStreamPlayer3D.xml

@@ -83,6 +83,9 @@
 		<member name="max_distance" type="float" setter="set_max_distance" getter="get_max_distance" default="0.0">
 			Sets the distance from which the [member out_of_range_mode] takes effect. Has no effect if set to 0.
 		</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.
+		</member>
 		<member name="out_of_range_mode" type="int" setter="set_out_of_range_mode" getter="get_out_of_range_mode" enum="AudioStreamPlayer3D.OutOfRangeMode" default="0">
 			Decides if audio should pause when source is outside of [member max_distance] range.
 		</member>

+ 4 - 0
modules/minimp3/audio_stream_mp3.cpp

@@ -214,6 +214,10 @@ float AudioStreamMP3::get_length() const {
 	return length;
 }
 
+bool AudioStreamMP3::is_monophonic() const {
+	return false;
+}
+
 void AudioStreamMP3::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamMP3::set_data);
 	ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamMP3::get_data);

+ 2 - 0
modules/minimp3/audio_stream_mp3.h

@@ -103,6 +103,8 @@ public:
 
 	virtual float get_length() const override;
 
+	virtual bool is_monophonic() const override;
+
 	AudioStreamMP3();
 	virtual ~AudioStreamMP3();
 };

+ 4 - 0
modules/stb_vorbis/audio_stream_ogg_vorbis.cpp

@@ -252,6 +252,10 @@ float AudioStreamOGGVorbis::get_length() const {
 	return length;
 }
 
+bool AudioStreamOGGVorbis::is_monophonic() const {
+	return false;
+}
+
 void AudioStreamOGGVorbis::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamOGGVorbis::set_data);
 	ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamOGGVorbis::get_data);

+ 2 - 0
modules/stb_vorbis/audio_stream_ogg_vorbis.h

@@ -105,6 +105,8 @@ public:
 
 	virtual float get_length() const override; //if supported, otherwise return 0
 
+	virtual bool is_monophonic() const override;
+
 	AudioStreamOGGVorbis();
 	virtual ~AudioStreamOGGVorbis();
 };

+ 93 - 69
scene/2d/audio_stream_player_2d.cpp

@@ -59,33 +59,47 @@ void AudioStreamPlayer2D::_notification(int p_what) {
 
 	if (p_what == 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())) {
+			_update_panning();
+		}
 
-		if (!stream_playback.is_valid()) {
-			return;
+		if (setplay.get() >= 0 && stream.is_valid()) {
+			active.set();
+			Ref<AudioStreamPlayback> new_playback = stream->instance_playback();
+			ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback.");
+			AudioServer::get_singleton()->start_playback_stream(new_playback, _get_actual_bus(), volume_vector, setplay.get());
+			stream_playbacks.push_back(new_playback);
+			setplay.set(-1);
 		}
-		if (setplay.get() >= 0 || (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count())) {
-			_update_panning();
-			if (setplay.get() >= 0) {
-				active.set();
-				AudioServer::get_singleton()->start_playback_stream(stream_playback, _get_actual_bus(), volume_vector, setplay.get());
-				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)) {
+					emit_signal(SNAME("finished"));
+					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);
 			}
 		}
 
-		// Stop playing if no longer active.
-		if (active.is_set() && !AudioServer::get_singleton()->is_playback_active(stream_playback)) {
-			active.clear();
-			set_physics_process_internal(false);
-			emit_signal(SNAME("finished"));
+		while (stream_playbacks.size() > max_polyphony) {
+			AudioServer::get_singleton()->stop_playback_stream(stream_playbacks[0]);
+			stream_playbacks.remove(0);
 		}
 	}
 }
 
 StringName AudioStreamPlayer2D::_get_actual_bus() {
-	if (!stream_playback.is_valid()) {
-		return SNAME("Master");
-	}
-
 	Vector2 global_pos = get_global_position();
 
 	//check if any area is diverting sound into a bus
@@ -113,12 +127,10 @@ StringName AudioStreamPlayer2D::_get_actual_bus() {
 }
 
 void AudioStreamPlayer2D::_update_panning() {
-	if (!stream_playback.is_valid()) {
+	if (!active.is_set() || stream.is_null()) {
 		return;
 	}
 
-	last_mix_count = AudioServer::get_singleton()->get_mix_count();
-
 	Ref<World2D> world_2d = get_world_2d();
 	ERR_FAIL_COND(world_2d.is_null());
 
@@ -164,28 +176,20 @@ void AudioStreamPlayer2D::_update_panning() {
 		volume_vector.write[0] = AudioFrame(l, r) * multiplier;
 	}
 
-	AudioServer::get_singleton()->set_playback_bus_exclusive(stream_playback, _get_actual_bus(), volume_vector);
-}
-
-void AudioStreamPlayer2D::set_stream(Ref<AudioStream> p_stream) {
-	if (stream_playback.is_valid()) {
-		stop();
+	for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+		AudioServer::get_singleton()->set_playback_bus_exclusive(playback, _get_actual_bus(), volume_vector);
 	}
 
-	stream_playback.unref();
-	stream.unref();
-	if (p_stream.is_valid()) {
-		stream_playback = p_stream->instance_playback();
-		if (stream_playback.is_valid()) {
-			stream = p_stream;
-		} else {
-			stream.unref();
-		}
+	for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+		AudioServer::get_singleton()->set_playback_pitch_scale(playback, pitch_scale);
 	}
 
-	if (p_stream.is_valid() && stream_playback.is_null()) {
-		stream.unref();
-	}
+	last_mix_count = AudioServer::get_singleton()->get_mix_count();
+}
+
+void AudioStreamPlayer2D::set_stream(Ref<AudioStream> p_stream) {
+	stop();
+	stream = p_stream;
 }
 
 Ref<AudioStream> AudioStreamPlayer2D::get_stream() const {
@@ -203,8 +207,8 @@ float AudioStreamPlayer2D::get_volume_db() const {
 void AudioStreamPlayer2D::set_pitch_scale(float p_pitch_scale) {
 	ERR_FAIL_COND(p_pitch_scale <= 0.0);
 	pitch_scale = p_pitch_scale;
-	if (stream_playback.is_valid()) {
-		AudioServer::get_singleton()->set_playback_pitch_scale(stream_playback, p_pitch_scale);
+	for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+		AudioServer::get_singleton()->set_playback_pitch_scale(playback, p_pitch_scale);
 	}
 }
 
@@ -213,44 +217,50 @@ float AudioStreamPlayer2D::get_pitch_scale() const {
 }
 
 void AudioStreamPlayer2D::play(float p_from_pos) {
-	stop();
-	if (stream.is_valid()) {
-		stream_playback = stream->instance_playback();
+	if (stream.is_null()) {
+		return;
 	}
-	if (stream_playback.is_valid()) {
-		setplay.set(p_from_pos);
-		set_physics_process_internal(true);
+	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();
 	}
+
+	setplay.set(p_from_pos);
+	active.set();
+	set_physics_process_internal(true);
 }
 
 void AudioStreamPlayer2D::seek(float p_seconds) {
-	if (stream_playback.is_valid() && active.is_set()) {
+	if (is_playing()) {
+		stop();
 		play(p_seconds);
 	}
 }
 
 void AudioStreamPlayer2D::stop() {
-	if (stream_playback.is_valid()) {
-		active.clear();
-		AudioServer::get_singleton()->stop_playback_stream(stream_playback);
-		set_physics_process_internal(false);
-		setplay.set(-1);
+	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);
 }
 
 bool AudioStreamPlayer2D::is_playing() const {
-	if (stream_playback.is_valid()) {
-		return AudioServer::get_singleton()->is_playback_active(stream_playback);
+	for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+		if (AudioServer::get_singleton()->is_playback_active(playback)) {
+			return true;
+		}
 	}
-
 	return false;
 }
 
 float AudioStreamPlayer2D::get_playback_position() {
-	if (stream_playback.is_valid()) {
-		return AudioServer::get_singleton()->get_playback_position(stream_playback);
+	// 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;
 }
 
@@ -284,11 +294,7 @@ void AudioStreamPlayer2D::_set_playing(bool p_enable) {
 }
 
 bool AudioStreamPlayer2D::_is_active() const {
-	if (stream_playback.is_valid()) {
-		// TODO make sure this doesn't change any behavior w.r.t. pauses. Is a paused stream active?
-		return AudioServer::get_singleton()->is_playback_active(stream_playback);
-	}
-	return false;
+	return active.is_set();
 }
 
 void AudioStreamPlayer2D::_validate_property(PropertyInfo &property) const {
@@ -336,21 +342,35 @@ 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 the stream isn't set, we can't persist this bool.
-	if (stream_playback.is_valid()) {
-		AudioServer::get_singleton()->set_playback_paused(stream_playback, 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 AudioStreamPlayer2D::get_stream_paused() const {
-	if (stream_playback.is_valid()) {
-		return AudioServer::get_singleton()->is_playback_paused(stream_playback);
+	// 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;
 }
 
 Ref<AudioStreamPlayback> AudioStreamPlayer2D::get_stream_playback() {
-	return stream_playback;
+	if (!stream_playbacks.is_empty()) {
+		return stream_playbacks[stream_playbacks.size() - 1];
+	}
+	return nullptr;
+}
+
+void AudioStreamPlayer2D::set_max_polyphony(int p_max_polyphony) {
+	if (p_max_polyphony > 0) {
+		max_polyphony = p_max_polyphony;
+	}
+}
+
+int AudioStreamPlayer2D::get_max_polyphony() const {
+	return max_polyphony;
 }
 
 void AudioStreamPlayer2D::_bind_methods() {
@@ -391,6 +411,9 @@ void AudioStreamPlayer2D::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer2D::set_stream_paused);
 	ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer2D::get_stream_paused);
 
+	ClassDB::bind_method(D_METHOD("set_max_polyphony", "max_polyphony"), &AudioStreamPlayer2D::set_max_polyphony);
+	ClassDB::bind_method(D_METHOD("get_max_polyphony"), &AudioStreamPlayer2D::get_max_polyphony);
+
 	ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer2D::get_stream_playback);
 
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
@@ -401,6 +424,7 @@ void AudioStreamPlayer2D::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused");
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_RANGE, "1,4096,1,or_greater,exp"), "set_max_distance", "get_max_distance");
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_attenuation", "get_attenuation");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "max_polyphony", PROPERTY_HINT_NONE, ""), "set_max_polyphony", "get_max_polyphony");
 	ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "area_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_area_mask", "get_area_mask");
 

+ 7 - 3
scene/2d/audio_stream_player_2d.h

@@ -51,10 +51,10 @@ private:
 		Viewport *viewport = nullptr; //pointer only used for reference to previous mix
 	};
 
-	Ref<AudioStreamPlayback> stream_playback;
+	Vector<Ref<AudioStreamPlayback>> stream_playbacks;
 	Ref<AudioStream> stream;
 
-	SafeFlag active;
+	SafeFlag active{ false };
 	SafeNumeric<float> setplay{ -1.0 };
 
 	Vector<AudioFrame> volume_vector;
@@ -64,7 +64,8 @@ private:
 	float volume_db = 0.0;
 	float pitch_scale = 1.0;
 	bool autoplay = false;
-	StringName default_bus = "Master";
+	StringName default_bus = SNAME("Master");
+	int max_polyphony = 1;
 
 	void _set_playing(bool p_enable);
 	bool _is_active() const;
@@ -119,6 +120,9 @@ public:
 	void set_stream_paused(bool p_pause);
 	bool get_stream_paused() const;
 
+	void set_max_polyphony(int p_max_polyphony);
+	int get_max_polyphony() const;
+
 	Ref<AudioStreamPlayback> get_stream_playback();
 
 	AudioStreamPlayer2D();

+ 100 - 65
scene/3d/audio_stream_player_3d.cpp

@@ -271,28 +271,45 @@ void AudioStreamPlayer3D::_notification(int p_what) {
 
 	if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
 		//update anything related to position first, if possible of course
-
-		if (!stream_playback.is_valid()) {
-			return;
+		Vector<AudioFrame> volume_vector;
+		if (setplay.get() > 0 || (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count())) {
+			volume_vector = _update_panning();
 		}
-		//start playing if requested
 
-		if (setplay.get() >= 0) {
-			Vector<AudioFrame> volume_vector = _update_panning();
-			AudioServer::get_singleton()->start_playback_stream(stream_playback, _get_actual_bus(), volume_vector, setplay.get());
+		if (setplay.get() >= 0 && stream.is_valid()) {
 			active.set();
+			Ref<AudioStreamPlayback> new_playback = stream->instance_playback();
+			ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback.");
+			Map<StringName, Vector<AudioFrame>> bus_map;
+			bus_map[_get_actual_bus()] = volume_vector;
+			AudioServer::get_singleton()->start_playback_stream(new_playback, bus_map, setplay.get(), linear_attenuation, attenuation_filter_cutoff_hz, actual_pitch_scale);
+			stream_playbacks.push_back(new_playback);
 			setplay.set(-1);
 		}
 
-		if (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count()) {
-			_update_panning();
-			last_mix_count = AudioServer::get_singleton()->get_mix_count();
+		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)) {
+					emit_signal(SNAME("finished"));
+					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);
+			}
 		}
 
-		// Stop playing if no longer active.
-		if (!active.is_set()) {
-			set_physics_process_internal(false);
-			emit_signal(SNAME("finished"));
+		while (stream_playbacks.size() > max_polyphony) {
+			AudioServer::get_singleton()->stop_playback_stream(stream_playbacks[0]);
+			stream_playbacks.remove(0);
 		}
 	}
 }
@@ -330,9 +347,6 @@ Area3D *AudioStreamPlayer3D::_get_overriding_area() {
 }
 
 StringName AudioStreamPlayer3D::_get_actual_bus() {
-	if (!stream_playback.is_valid()) {
-		return SNAME("Master");
-	}
 	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();
@@ -347,7 +361,9 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
 		frame = AudioFrame(0, 0);
 	}
 
-	ERR_FAIL_COND_V(stream_playback.is_null(), output_volume_vector);
+	if (!active.is_set() || stream.is_null()) {
+		return output_volume_vector;
+	}
 
 	Vector3 linear_velocity;
 
@@ -422,7 +438,10 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
 			}
 		}
 
-		AudioServer::get_singleton()->set_playback_highshelf_params(stream_playback, Math::db2linear(db_att), attenuation_filter_cutoff_hz);
+		linear_attenuation = Math::db2linear(db_att);
+		for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+			AudioServer::get_singleton()->set_playback_highshelf_params(playback, linear_attenuation, attenuation_filter_cutoff_hz);
+		}
 		//TODO: The lower the second parameter (tightness) the more the sound will "enclose" the listener (more undirected / playing from
 		//      speakers not facing the source) - this could be made distance dependent.
 		_calc_output_vol(local_pos.normalized(), 4.0, output_volume_vector);
@@ -447,7 +466,10 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
 		} else {
 			bus_volumes[bus] = output_volume_vector;
 		}
-		AudioServer::get_singleton()->set_playback_bus_volumes_linear(stream_playback, bus_volumes);
+
+		for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+			AudioServer::get_singleton()->set_playback_bus_volumes_linear(playback, bus_volumes);
+		}
 
 		if (doppler_tracking != DOPPLER_TRACKING_DISABLED) {
 			Vector3 listener_velocity;
@@ -458,9 +480,7 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
 
 			Vector3 local_velocity = listener_node->get_global_transform().orthonormalized().basis.xform_inv(linear_velocity - listener_velocity);
 
-			if (local_velocity == Vector3()) {
-				AudioServer::get_singleton()->set_playback_pitch_scale(stream_playback, pitch_scale);
-			} else {
+			if (local_velocity != Vector3()) {
 				float approaching = local_pos.normalized().dot(local_velocity.normalized());
 				float velocity = local_velocity.length();
 				float speed_of_sound = 343.0;
@@ -468,34 +488,23 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
 				float doppler_pitch_scale = 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
 
-				AudioServer::get_singleton()->set_playback_pitch_scale(stream_playback, doppler_pitch_scale);
+				actual_pitch_scale = doppler_pitch_scale;
+			} else {
+				actual_pitch_scale = pitch_scale;
 			}
 		} else {
-			AudioServer::get_singleton()->set_playback_pitch_scale(stream_playback, pitch_scale);
+			actual_pitch_scale = pitch_scale;
+		}
+		for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+			AudioServer::get_singleton()->set_playback_pitch_scale(playback, actual_pitch_scale);
 		}
 	}
 	return output_volume_vector;
 }
 
 void AudioStreamPlayer3D::set_stream(Ref<AudioStream> p_stream) {
-	if (stream_playback.is_valid()) {
-		stop();
-		stream_playback.unref();
-		stream.unref();
-	}
-
-	if (p_stream.is_valid()) {
-		stream_playback = p_stream->instance_playback();
-		if (stream_playback.is_valid()) {
-			stream = p_stream;
-		} else {
-			stream.unref();
-		}
-	}
-
-	if (p_stream.is_valid() && stream_playback.is_null()) {
-		stream.unref();
-	}
+	stop();
+	stream = p_stream;
 }
 
 Ref<AudioStream> AudioStreamPlayer3D::get_stream() const {
@@ -536,40 +545,47 @@ float AudioStreamPlayer3D::get_pitch_scale() const {
 }
 
 void AudioStreamPlayer3D::play(float p_from_pos) {
-	if (stream_playback.is_valid()) {
-		setplay.set(p_from_pos);
-		set_physics_process_internal(true);
+	if (stream.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();
 	}
+	setplay.set(p_from_pos);
+	active.set();
+	set_physics_process_internal(true);
 }
 
 void AudioStreamPlayer3D::seek(float p_seconds) {
-	if (stream_playback.is_valid() && active.is_set()) {
-		play(p_seconds);
-	}
+	stop();
+	play(p_seconds);
 }
 
 void AudioStreamPlayer3D::stop() {
-	if (stream_playback.is_valid()) {
-		active.clear();
-		AudioServer::get_singleton()->stop_playback_stream(stream_playback);
-		set_physics_process_internal(false);
-		setplay.set(-1);
+	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);
 }
 
 bool AudioStreamPlayer3D::is_playing() const {
-	if (stream_playback.is_valid()) {
-		return active.is_set() || setplay.get() >= 0;
+	for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+		if (AudioServer::get_singleton()->is_playback_active(playback)) {
+			return true;
+		}
 	}
-
 	return false;
 }
 
 float AudioStreamPlayer3D::get_playback_position() {
-	if (stream_playback.is_valid()) {
-		return AudioServer::get_singleton()->get_playback_position(stream_playback);
+	// 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;
 }
 
@@ -729,20 +745,35 @@ AudioStreamPlayer3D::DopplerTracking AudioStreamPlayer3D::get_doppler_tracking()
 }
 
 void AudioStreamPlayer3D::set_stream_paused(bool p_pause) {
-	if (stream_playback.is_valid()) {
-		AudioServer::get_singleton()->set_playback_paused(stream_playback, 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 AudioStreamPlayer3D::get_stream_paused() const {
-	if (stream_playback.is_valid()) {
-		return AudioServer::get_singleton()->is_playback_paused(stream_playback);
+	// 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;
 }
 
 Ref<AudioStreamPlayback> AudioStreamPlayer3D::get_stream_playback() {
-	return stream_playback;
+	if (!stream_playbacks.is_empty()) {
+		return stream_playbacks[stream_playbacks.size() - 1];
+	}
+	return nullptr;
+}
+
+void AudioStreamPlayer3D::set_max_polyphony(int p_max_polyphony) {
+	if (p_max_polyphony > 0) {
+		max_polyphony = p_max_polyphony;
+	}
+}
+
+int AudioStreamPlayer3D::get_max_polyphony() const {
+	return max_polyphony;
 }
 
 void AudioStreamPlayer3D::_bind_methods() {
@@ -810,6 +841,9 @@ void AudioStreamPlayer3D::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer3D::set_stream_paused);
 	ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer3D::get_stream_paused);
 
+	ClassDB::bind_method(D_METHOD("set_max_polyphony", "max_polyphony"), &AudioStreamPlayer3D::set_max_polyphony);
+	ClassDB::bind_method(D_METHOD("get_max_polyphony"), &AudioStreamPlayer3D::get_max_polyphony);
+
 	ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer3D::get_stream_playback);
 
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
@@ -823,6 +857,7 @@ void AudioStreamPlayer3D::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused");
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_RANGE, "0,4096,1,or_greater,exp"), "set_max_distance", "get_max_distance");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "out_of_range_mode", PROPERTY_HINT_ENUM, "Mix,Pause"), "set_out_of_range_mode", "get_out_of_range_mode");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "max_polyphony", PROPERTY_HINT_NONE, ""), "set_max_polyphony", "get_max_polyphony");
 	ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "area_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_area_mask", "get_area_mask");
 	ADD_GROUP("Emission Angle", "emission_angle");

+ 12 - 3
scene/3d/audio_stream_player_3d.h

@@ -31,6 +31,7 @@
 #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"
@@ -68,10 +69,10 @@ private:
 
 	};
 
-	Ref<AudioStreamPlayback> stream_playback;
+	Vector<Ref<AudioStreamPlayback>> stream_playbacks;
 	Ref<AudioStream> stream;
 
-	SafeFlag active;
+	SafeFlag active{ false };
 	SafeNumeric<float> setplay{ -1.0 };
 
 	AttenuationModel attenuation_model = ATTENUATION_INVERSE_DISTANCE;
@@ -79,8 +80,11 @@ private:
 	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 = "Master";
+	StringName bus = SNAME("Master");
+	int max_polyphony = 1;
 
 	uint64_t last_mix_count = -1;
 
@@ -106,6 +110,8 @@ private:
 	float attenuation_filter_cutoff_hz = 5000.0;
 	float attenuation_filter_db = -24.0;
 
+	float linear_attenuation = 0;
+
 	float max_distance = 0.0;
 
 	Ref<VelocityTracker3D> velocity_tracker;
@@ -146,6 +152,9 @@ public:
 	void set_bus(const StringName &p_bus);
 	StringName get_bus() const;
 
+	void set_max_polyphony(int p_max_polyphony);
+	int get_max_polyphony() const;
+
 	void set_autoplay(bool p_enable);
 	bool is_autoplay_enabled();
 

+ 86 - 51
scene/audio/audio_stream_player.cpp

@@ -42,17 +42,29 @@ void AudioStreamPlayer::_notification(int p_what) {
 	}
 
 	if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
-		if (stream_playback.is_valid() && active.is_set() && !AudioServer::get_singleton()->is_playback_active(stream_playback)) {
+		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)) {
+				emit_signal(SNAME("finished"));
+				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);
-			emit_signal(SNAME("finished"));
 		}
 	}
 
 	if (p_what == NOTIFICATION_EXIT_TREE) {
-		if (stream_playback.is_valid()) {
-			AudioServer::get_singleton()->stop_playback_stream(stream_playback);
+		for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+			AudioServer::get_singleton()->stop_playback_stream(playback);
 		}
+		stream_playbacks.clear();
 	}
 
 	if (p_what == NOTIFICATION_PAUSED) {
@@ -68,24 +80,8 @@ void AudioStreamPlayer::_notification(int p_what) {
 }
 
 void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) {
-	if (stream_playback.is_valid()) {
-		stop();
-		stream_playback.unref();
-		stream.unref();
-	}
-
-	if (p_stream.is_valid()) {
-		stream_playback = p_stream->instance_playback();
-		if (stream_playback.is_valid()) {
-			stream = p_stream;
-		} else {
-			stream.unref();
-		}
-	}
-
-	if (p_stream.is_valid() && stream_playback.is_null()) {
-		stream.unref();
-	}
+	stop();
+	stream = p_stream;
 }
 
 Ref<AudioStream> AudioStreamPlayer::get_stream() const {
@@ -95,7 +91,10 @@ Ref<AudioStream> AudioStreamPlayer::get_stream() const {
 void AudioStreamPlayer::set_volume_db(float p_volume) {
 	volume_db = p_volume;
 
-	AudioServer::get_singleton()->set_playback_all_bus_volumes_linear(stream_playback, _get_volume_vector());
+	Vector<AudioFrame> volume_vector = _get_volume_vector();
+	for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+		AudioServer::get_singleton()->set_playback_all_bus_volumes_linear(playback, volume_vector);
+	}
 }
 
 float AudioStreamPlayer::get_volume_db() const {
@@ -106,57 +105,83 @@ void AudioStreamPlayer::set_pitch_scale(float p_pitch_scale) {
 	ERR_FAIL_COND(p_pitch_scale <= 0.0);
 	pitch_scale = p_pitch_scale;
 
-	AudioServer::get_singleton()->set_playback_pitch_scale(stream_playback, pitch_scale);
+	for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+		AudioServer::get_singleton()->set_playback_pitch_scale(playback, pitch_scale);
+	}
 }
 
 float AudioStreamPlayer::get_pitch_scale() const {
 	return pitch_scale;
 }
 
+void AudioStreamPlayer::set_max_polyphony(int p_max_polyphony) {
+	if (p_max_polyphony > 0) {
+		max_polyphony = p_max_polyphony;
+	}
+}
+
+int AudioStreamPlayer::get_max_polyphony() const {
+	return max_polyphony;
+}
+
 void AudioStreamPlayer::play(float p_from_pos) {
-	stop();
-	if (stream.is_valid()) {
-		stream_playback = stream->instance_playback();
+	if (stream.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();
 	}
-	if (stream_playback.is_valid()) {
-		AudioServer::get_singleton()->start_playback_stream(stream_playback, bus, _get_volume_vector(), p_from_pos);
-		active.set();
+	Ref<AudioStreamPlayback> stream_playback = stream->instance_playback();
+	ERR_FAIL_COND_MSG(stream_playback.is_null(), "Failed to instantiate playback.");
+
+	AudioServer::get_singleton()->start_playback_stream(stream_playback, bus, _get_volume_vector(), p_from_pos);
+	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(0);
 	}
 }
 
 void AudioStreamPlayer::seek(float p_seconds) {
-	if (stream_playback.is_valid() && active.is_set()) {
+	if (is_playing()) {
+		stop();
 		play(p_seconds);
 	}
 }
 
 void AudioStreamPlayer::stop() {
-	if (stream_playback.is_valid()) {
-		active.clear();
-		AudioServer::get_singleton()->stop_playback_stream(stream_playback);
+	for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+		AudioServer::get_singleton()->stop_playback_stream(playback);
 	}
+	stream_playbacks.clear();
+	active.clear();
+	set_process_internal(false);
 }
 
 bool AudioStreamPlayer::is_playing() const {
-	if (stream_playback.is_valid()) {
-		return AudioServer::get_singleton()->is_playback_active(stream_playback);
+	for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+		if (AudioServer::get_singleton()->is_playback_active(playback)) {
+			return true;
+		}
 	}
-
 	return false;
 }
 
 float AudioStreamPlayer::get_playback_position() {
-	if (stream_playback.is_valid()) {
-		return AudioServer::get_singleton()->get_playback_position(stream_playback);
+	// 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 AudioStreamPlayer::set_bus(const StringName &p_bus) {
 	bus = p_bus;
-	if (stream_playback.is_valid()) {
-		AudioServer::get_singleton()->set_playback_bus_exclusive(stream_playback, p_bus, _get_volume_vector());
+	for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+		AudioServer::get_singleton()->set_playback_bus_exclusive(playback, p_bus, _get_volume_vector());
 	}
 }
 
@@ -166,7 +191,7 @@ StringName AudioStreamPlayer::get_bus() const {
 			return bus;
 		}
 	}
-	return "Master";
+	return SNAME("Master");
 }
 
 void AudioStreamPlayer::set_autoplay(bool p_enable) {
@@ -194,22 +219,25 @@ void AudioStreamPlayer::_set_playing(bool p_enable) {
 }
 
 bool AudioStreamPlayer::_is_active() const {
-	if (stream_playback.is_valid()) {
-		return AudioServer::get_singleton()->is_playback_active(stream_playback);
+	for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+		if (AudioServer::get_singleton()->is_playback_active(playback)) {
+			return true;
+		}
 	}
 	return false;
 }
 
 void AudioStreamPlayer::set_stream_paused(bool p_pause) {
-	// TODO this does not have perfect recall, fix that maybe? If the stream isn't set, we can't persist this bool.
-	if (stream_playback.is_valid()) {
-		AudioServer::get_singleton()->set_playback_paused(stream_playback, 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 AudioStreamPlayer::get_stream_paused() const {
-	if (stream_playback.is_valid()) {
-		return AudioServer::get_singleton()->is_playback_paused(stream_playback);
+	// 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;
 }
@@ -271,7 +299,10 @@ void AudioStreamPlayer::_bus_layout_changed() {
 }
 
 Ref<AudioStreamPlayback> AudioStreamPlayer::get_stream_playback() {
-	return stream_playback;
+	if (!stream_playbacks.is_empty()) {
+		return stream_playbacks[stream_playbacks.size() - 1];
+	}
+	return nullptr;
 }
 
 void AudioStreamPlayer::_bind_methods() {
@@ -306,6 +337,9 @@ void AudioStreamPlayer::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer::set_stream_paused);
 	ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer::get_stream_paused);
 
+	ClassDB::bind_method(D_METHOD("set_max_polyphony", "max_polyphony"), &AudioStreamPlayer::set_max_polyphony);
+	ClassDB::bind_method(D_METHOD("get_max_polyphony"), &AudioStreamPlayer::get_max_polyphony);
+
 	ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer::get_stream_playback);
 
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
@@ -315,6 +349,7 @@ void AudioStreamPlayer::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_target", PROPERTY_HINT_ENUM, "Stereo,Surround,Center"), "set_mix_target", "get_mix_target");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "max_polyphony", PROPERTY_HINT_NONE, ""), "set_max_polyphony", "get_max_polyphony");
 	ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");
 
 	ADD_SIGNAL(MethodInfo("finished"));

+ 6 - 2
scene/audio/audio_stream_player.h

@@ -46,7 +46,7 @@ public:
 	};
 
 private:
-	Ref<AudioStreamPlayback> stream_playback;
+	Vector<Ref<AudioStreamPlayback>> stream_playbacks;
 	Ref<AudioStream> stream;
 
 	SafeFlag active;
@@ -54,7 +54,8 @@ private:
 	float pitch_scale = 1.0;
 	float volume_db = 0.0;
 	bool autoplay = false;
-	StringName bus = "Master";
+	StringName bus = SNAME("Master");
+	int max_polyphony = 1;
 
 	MixTarget mix_target = MIX_TARGET_STEREO;
 
@@ -85,6 +86,9 @@ public:
 	void set_pitch_scale(float p_pitch_scale);
 	float get_pitch_scale() const;
 
+	void set_max_polyphony(int p_max_polyphony);
+	int get_max_polyphony() const;
+
 	void play(float p_from_pos = 0.0);
 	void seek(float p_seconds);
 	void stop();

+ 4 - 0
scene/resources/audio_stream_sample.cpp

@@ -480,6 +480,10 @@ float AudioStreamSample::get_length() const {
 	return float(len) / mix_rate;
 }
 
+bool AudioStreamSample::is_monophonic() const {
+	return false;
+}
+
 void AudioStreamSample::set_data(const Vector<uint8_t> &p_data) {
 	AudioServer::get_singleton()->lock();
 	if (data) {

+ 2 - 0
scene/resources/audio_stream_sample.h

@@ -136,6 +136,8 @@ public:
 
 	virtual float get_length() const override; //if supported, otherwise return 0
 
+	virtual bool is_monophonic() const override;
+
 	void set_data(const Vector<uint8_t> &p_data);
 	Vector<uint8_t> get_data() const;
 

+ 22 - 0
servers/audio/audio_stream.cpp

@@ -189,11 +189,21 @@ float AudioStream::get_length() const {
 	return 0;
 }
 
+bool AudioStream::is_monophonic() const {
+	bool ret;
+	if (GDVIRTUAL_CALL(_is_monophonic, ret)) {
+		return ret;
+	}
+	return true;
+}
+
 void AudioStream::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_length"), &AudioStream::get_length);
+	ClassDB::bind_method(D_METHOD("is_monophonic"), &AudioStream::is_monophonic);
 	GDVIRTUAL_BIND(_instance_playback);
 	GDVIRTUAL_BIND(_get_stream_name);
 	GDVIRTUAL_BIND(_get_length);
+	GDVIRTUAL_BIND(_is_monophonic);
 }
 
 ////////////////////////////////
@@ -221,6 +231,10 @@ float AudioStreamMicrophone::get_length() const {
 	return 0;
 }
 
+bool AudioStreamMicrophone::is_monophonic() const {
+	return true;
+}
+
 void AudioStreamMicrophone::_bind_methods() {
 }
 
@@ -388,6 +402,14 @@ float AudioStreamRandomPitch::get_length() const {
 	return 0;
 }
 
+bool AudioStreamRandomPitch::is_monophonic() const {
+	if (audio_stream.is_valid()) {
+		return audio_stream->is_monophonic();
+	}
+
+	return true; // It doesn't really matter what we return here, but no sense instancing a many playbacks of a null stream.
+}
+
 void AudioStreamRandomPitch::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_audio_stream", "stream"), &AudioStreamRandomPitch::set_audio_stream);
 	ClassDB::bind_method(D_METHOD("get_audio_stream"), &AudioStreamRandomPitch::get_audio_stream);

+ 5 - 0
servers/audio/audio_stream.h

@@ -102,12 +102,14 @@ protected:
 	GDVIRTUAL0RC(Ref<AudioStreamPlayback>, _instance_playback)
 	GDVIRTUAL0RC(String, _get_stream_name)
 	GDVIRTUAL0RC(float, _get_length)
+	GDVIRTUAL0RC(bool, _is_monophonic)
 
 public:
 	virtual Ref<AudioStreamPlayback> instance_playback();
 	virtual String get_stream_name() const;
 
 	virtual float get_length() const;
+	virtual bool is_monophonic() const;
 };
 
 // Microphone
@@ -129,6 +131,8 @@ public:
 
 	virtual float get_length() const override; //if supported, otherwise return 0
 
+	virtual bool is_monophonic() const override;
+
 	AudioStreamMicrophone();
 };
 
@@ -187,6 +191,7 @@ public:
 	virtual String get_stream_name() const override;
 
 	virtual float get_length() const override; //if supported, otherwise return 0
+	virtual bool is_monophonic() const override;
 
 	AudioStreamRandomPitch();
 };

+ 4 - 0
servers/audio/effects/audio_stream_generator.cpp

@@ -64,6 +64,10 @@ float AudioStreamGenerator::get_length() const {
 	return 0;
 }
 
+bool AudioStreamGenerator::is_monophonic() const {
+	return true;
+}
+
 void AudioStreamGenerator::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_mix_rate", "hz"), &AudioStreamGenerator::set_mix_rate);
 	ClassDB::bind_method(D_METHOD("get_mix_rate"), &AudioStreamGenerator::get_mix_rate);

+ 1 - 0
servers/audio/effects/audio_stream_generator.h

@@ -54,6 +54,7 @@ public:
 	virtual String get_stream_name() const override;
 
 	virtual float get_length() const override;
+	virtual bool is_monophonic() const override;
 	AudioStreamGenerator();
 };
 

+ 44 - 44
servers/audio_server.cpp

@@ -363,10 +363,10 @@ void AudioServer::_mix_step() {
 		if (mixed_frames != buffer_size) {
 			// We know we have at least the size of our lookahead buffer for fade-out purposes.
 
-			float fadeout_base = 0.87;
+			float fadeout_base = 0.94;
 			float fadeout_coefficient = 1;
-			static_assert(LOOKAHEAD_BUFFER_SIZE == 32, "Update fadeout_base and comment here if you change LOOKAHEAD_BUFFER_SIZE.");
-			// 0.87 ^ 32 = 0.0116. There might still be a pop but it'll be way better than if we didn't do this.
+			static_assert(LOOKAHEAD_BUFFER_SIZE == 64, "Update fadeout_base and comment here if you change LOOKAHEAD_BUFFER_SIZE.");
+			// 0.94 ^ 64 = 0.01906. There might still be a pop but it'll be way better than if we didn't do this.
 			for (unsigned int idx = mixed_frames; idx < buffer_size; idx++) {
 				fadeout_coefficient *= fadeout_base;
 				buf[idx] *= fadeout_coefficient;
@@ -381,29 +381,24 @@ void AudioServer::_mix_step() {
 			}
 		}
 
-		ERR_FAIL_COND(playback->bus_details.load() == nullptr);
+		AudioStreamPlaybackBusDetails *ptr = playback->bus_details.load();
+		ERR_FAIL_COND(ptr == nullptr);
 		// By putting null into the bus details pointers, we're taking ownership of their memory for the duration of this mix.
-		AudioStreamPlaybackBusDetails *bus_details = nullptr;
-		{
-			std::atomic<AudioStreamPlaybackBusDetails *> bus_details_atomic = nullptr;
-			bus_details = playback->bus_details.exchange(bus_details_atomic);
-		}
-		ERR_FAIL_COND(bus_details == nullptr);
-		AudioStreamPlaybackBusDetails *prev_bus_details = playback->prev_bus_details;
+		AudioStreamPlaybackBusDetails bus_details = *ptr;
 
 		// Mix to any active buses.
 		for (int idx = 0; idx < MAX_BUSES_PER_PLAYBACK; idx++) {
-			if (!bus_details->bus_active[idx]) {
+			if (!bus_details.bus_active[idx]) {
 				continue;
 			}
-			int bus_idx = thread_find_bus_index(bus_details->bus[idx]);
+			int bus_idx = thread_find_bus_index(bus_details.bus[idx]);
 
 			int prev_bus_idx = -1;
 			for (int search_idx = 0; search_idx < MAX_BUSES_PER_PLAYBACK; search_idx++) {
-				if (!prev_bus_details->bus_active[search_idx]) {
+				if (!playback->prev_bus_details->bus_active[search_idx]) {
 					continue;
 				}
-				if (prev_bus_details->bus[search_idx].hash() == bus_details->bus[idx].hash()) {
+				if (playback->prev_bus_details->bus[search_idx].hash() == bus_details.bus[idx].hash()) {
 					prev_bus_idx = search_idx;
 				}
 			}
@@ -411,13 +406,13 @@ void AudioServer::_mix_step() {
 			for (int channel_idx = 0; channel_idx < channel_count; channel_idx++) {
 				AudioFrame *channel_buf = thread_get_channel_mix_buffer(bus_idx, channel_idx);
 				if (fading_out) {
-					bus_details->volume[idx][channel_idx] = AudioFrame(0, 0);
+					bus_details.volume[idx][channel_idx] = AudioFrame(0, 0);
 				}
-				AudioFrame channel_vol = bus_details->volume[idx][channel_idx];
+				AudioFrame channel_vol = bus_details.volume[idx][channel_idx];
 
 				AudioFrame prev_channel_vol = AudioFrame(0, 0);
 				if (prev_bus_idx != -1) {
-					prev_channel_vol = prev_bus_details->volume[prev_bus_idx][channel_idx];
+					prev_channel_vol = playback->prev_bus_details->volume[prev_bus_idx][channel_idx];
 				}
 				_mix_step_for_channel(channel_buf, buf, prev_channel_vol, channel_vol, playback->attenuation_filter_cutoff_hz.get(), playback->highshelf_gain.get(), &playback->filter_process[channel_idx * 2], &playback->filter_process[channel_idx * 2 + 1]);
 			}
@@ -425,14 +420,14 @@ void AudioServer::_mix_step() {
 
 		// Now go through and fade-out any buses that were being played to previously that we missed by going through current data.
 		for (int idx = 0; idx < MAX_BUSES_PER_PLAYBACK; idx++) {
-			if (!prev_bus_details->bus_active[idx]) {
+			if (!playback->prev_bus_details->bus_active[idx]) {
 				continue;
 			}
-			int bus_idx = thread_find_bus_index(prev_bus_details->bus[idx]);
+			int bus_idx = thread_find_bus_index(playback->prev_bus_details->bus[idx]);
 
 			int current_bus_idx = -1;
 			for (int search_idx = 0; search_idx < MAX_BUSES_PER_PLAYBACK; search_idx++) {
-				if (bus_details->bus[search_idx] == prev_bus_details->bus[idx]) {
+				if (bus_details.bus[search_idx] == playback->prev_bus_details->bus[idx]) {
 					current_bus_idx = search_idx;
 				}
 			}
@@ -443,24 +438,17 @@ void AudioServer::_mix_step() {
 
 			for (int channel_idx = 0; channel_idx < channel_count; channel_idx++) {
 				AudioFrame *channel_buf = thread_get_channel_mix_buffer(bus_idx, channel_idx);
-				AudioFrame prev_channel_vol = prev_bus_details->volume[idx][channel_idx];
+				AudioFrame prev_channel_vol = playback->prev_bus_details->volume[idx][channel_idx];
 				// Fade out to silence
 				_mix_step_for_channel(channel_buf, buf, prev_channel_vol, AudioFrame(0, 0), playback->attenuation_filter_cutoff_hz.get(), playback->highshelf_gain.get(), &playback->filter_process[channel_idx * 2], &playback->filter_process[channel_idx * 2 + 1]);
 			}
 		}
 
 		// Copy the bus details we mixed with to the previous bus details to maintain volume ramps.
-		std::copy(std::begin(bus_details->bus_active), std::end(bus_details->bus_active), std::begin(prev_bus_details->bus_active));
-		std::copy(std::begin(bus_details->bus), std::end(bus_details->bus), std::begin(prev_bus_details->bus));
+		std::copy(std::begin(bus_details.bus_active), std::end(bus_details.bus_active), std::begin(playback->prev_bus_details->bus_active));
+		std::copy(std::begin(bus_details.bus), std::end(bus_details.bus), std::begin(playback->prev_bus_details->bus));
 		for (int bus_idx = 0; bus_idx < MAX_BUSES_PER_PLAYBACK; bus_idx++) {
-			std::copy(std::begin(bus_details->volume[bus_idx]), std::end(bus_details->volume[bus_idx]), std::begin(prev_bus_details->volume[bus_idx]));
-		}
-
-		AudioStreamPlaybackBusDetails *bus_details_expected = nullptr;
-		// Only put the bus details pointer back if it hasn't been updated already.
-		if (!playback->bus_details.compare_exchange_strong(/* expected= */ bus_details_expected, /* new= */ bus_details)) {
-			// If it *has* been updated already, queue the old one for deletion.
-			bus_details_graveyard.insert(bus_details);
+			std::copy(std::begin(bus_details.volume[bus_idx]), std::end(bus_details.volume[bus_idx]), std::begin(playback->prev_bus_details->volume[bus_idx]));
 		}
 
 		switch (playback->state.load()) {
@@ -1132,7 +1120,7 @@ void AudioServer::start_playback_stream(Ref<AudioStreamPlayback> p_playback, Str
 	start_playback_stream(p_playback, map, p_start_time);
 }
 
-void AudioServer::start_playback_stream(Ref<AudioStreamPlayback> p_playback, Map<StringName, Vector<AudioFrame>> p_bus_volumes, float p_start_time) {
+void AudioServer::start_playback_stream(Ref<AudioStreamPlayback> p_playback, Map<StringName, Vector<AudioFrame>> p_bus_volumes, float p_start_time, float p_highshelf_gain, float p_attenuation_cutoff_hz, float p_pitch_scale) {
 	ERR_FAIL_COND(p_playback.is_null());
 
 	AudioStreamPlaybackListNode *playback_node = new AudioStreamPlaybackListNode();
@@ -1154,10 +1142,9 @@ void AudioServer::start_playback_stream(Ref<AudioStreamPlayback> p_playback, Map
 	playback_node->bus_details = new_bus_details;
 	playback_node->prev_bus_details = new AudioStreamPlaybackBusDetails();
 
-	playback_node->setseek.set(-1);
-	playback_node->pitch_scale.set(1);
-	playback_node->highshelf_gain.set(0);
-	playback_node->attenuation_filter_cutoff_hz.set(0);
+	playback_node->pitch_scale.set(p_pitch_scale);
+	playback_node->highshelf_gain.set(p_highshelf_gain);
+	playback_node->attenuation_filter_cutoff_hz.set(p_attenuation_cutoff_hz);
 
 	memset(playback_node->prev_bus_details->volume, 0, sizeof(playback_node->prev_bus_details->volume));
 
@@ -1181,6 +1168,9 @@ void AudioServer::stop_playback_stream(Ref<AudioStreamPlayback> p_playback) {
 	AudioStreamPlaybackListNode::PlaybackState new_state, old_state;
 	do {
 		old_state = playback_node->state.load();
+		if (old_state == AudioStreamPlaybackListNode::AWAITING_DELETION) {
+			break; // Don't fade out again.
+		}
 		new_state = AudioStreamPlaybackListNode::FADE_OUT_TO_DELETION;
 
 	} while (!playback_node->state.compare_exchange_strong(old_state, new_state));
@@ -1206,6 +1196,9 @@ void AudioServer::set_playback_bus_volumes_linear(Ref<AudioStreamPlayback> p_pla
 
 	int idx = 0;
 	for (KeyValue<StringName, Vector<AudioFrame>> pair : p_bus_volumes) {
+		if (idx >= MAX_BUSES_PER_PLAYBACK) {
+			break;
+		}
 		ERR_FAIL_COND(pair.value.size() < channel_count);
 		ERR_FAIL_COND(pair.value.size() != MAX_CHANNELS_PER_BUS);
 
@@ -1214,6 +1207,7 @@ void AudioServer::set_playback_bus_volumes_linear(Ref<AudioStreamPlayback> p_pla
 		for (int channel_idx = 0; channel_idx < MAX_CHANNELS_PER_BUS; channel_idx++) {
 			new_bus_details->volume[idx][channel_idx] = pair.value[channel_idx];
 		}
+		idx++;
 	}
 
 	do {
@@ -1260,17 +1254,18 @@ void AudioServer::set_playback_paused(Ref<AudioStreamPlayback> p_playback, bool
 	if (!playback_node) {
 		return;
 	}
-	if (!p_paused && playback_node->state == AudioStreamPlaybackListNode::PLAYING) {
-		return; // No-op.
-	}
-	if (p_paused && (playback_node->state == AudioStreamPlaybackListNode::PAUSED || playback_node->state == AudioStreamPlaybackListNode::FADE_OUT_TO_PAUSE)) {
-		return; // No-op.
-	}
 
 	AudioStreamPlaybackListNode::PlaybackState new_state, old_state;
 	do {
 		old_state = playback_node->state.load();
 		new_state = p_paused ? AudioStreamPlaybackListNode::FADE_OUT_TO_PAUSE : AudioStreamPlaybackListNode::PLAYING;
+		if (!p_paused && old_state == AudioStreamPlaybackListNode::PLAYING) {
+			return; // No-op.
+		}
+		if (p_paused && (old_state == AudioStreamPlaybackListNode::PAUSED || old_state == AudioStreamPlaybackListNode::FADE_OUT_TO_PAUSE)) {
+			return; // No-op.
+		}
+
 	} while (!playback_node->state.compare_exchange_strong(old_state, new_state));
 }
 
@@ -1444,10 +1439,15 @@ void AudioServer::update() {
 	update_callback_list.maybe_cleanup();
 	listener_changed_callback_list.maybe_cleanup();
 	playback_list.maybe_cleanup();
+	for (AudioStreamPlaybackBusDetails *bus_details : bus_details_graveyard_frame_old) {
+		bus_details_graveyard_frame_old.erase(bus_details, [](AudioStreamPlaybackBusDetails *d) { delete d; });
+	}
 	for (AudioStreamPlaybackBusDetails *bus_details : bus_details_graveyard) {
-		bus_details_graveyard.erase(bus_details, [](AudioStreamPlaybackBusDetails *d) { delete d; });
+		bus_details_graveyard_frame_old.insert(bus_details);
+		bus_details_graveyard.erase(bus_details);
 	}
 	bus_details_graveyard.maybe_cleanup();
+	bus_details_graveyard_frame_old.maybe_cleanup();
 }
 
 void AudioServer::load_default_bus_layout() {

+ 7 - 2
servers/audio_server.h

@@ -163,7 +163,7 @@ public:
 		AUDIO_DATA_INVALID_ID = -1,
 		MAX_CHANNELS_PER_BUS = 4,
 		MAX_BUSES_PER_PLAYBACK = 6,
-		LOOKAHEAD_BUFFER_SIZE = 32,
+		LOOKAHEAD_BUFFER_SIZE = 64,
 	};
 
 	typedef void (*AudioCallback)(void *p_userdata);
@@ -262,6 +262,9 @@ private:
 	SafeList<AudioStreamPlaybackListNode *> playback_list;
 	SafeList<AudioStreamPlaybackBusDetails *> bus_details_graveyard;
 
+	// TODO document if this is necessary.
+	SafeList<AudioStreamPlaybackBusDetails *> bus_details_graveyard_frame_old;
+
 	Vector<Vector<AudioFrame>> temp_buffer; //temp_buffer for each level
 	Vector<AudioFrame> mix_buffer;
 	Vector<Bus *> buses;
@@ -364,8 +367,10 @@ public:
 	void set_playback_speed_scale(float p_scale);
 	float get_playback_speed_scale() const;
 
+	// Convenience method.
 	void start_playback_stream(Ref<AudioStreamPlayback> p_playback, StringName p_bus, Vector<AudioFrame> p_volume_db_vector, float p_start_time = 0);
-	void start_playback_stream(Ref<AudioStreamPlayback> p_playback, Map<StringName, Vector<AudioFrame>> p_bus_volumes, float p_start_time = 0);
+	// Expose all parameters.
+	void start_playback_stream(Ref<AudioStreamPlayback> p_playback, Map<StringName, Vector<AudioFrame>> p_bus_volumes, float p_start_time = 0, float p_highshelf_gain = 0, float p_attenuation_cutoff_hz = 0, float p_pitch_scale = 1);
 	void stop_playback_stream(Ref<AudioStreamPlayback> p_playback);
 
 	void set_playback_bus_exclusive(Ref<AudioStreamPlayback> p_playback, StringName p_bus, Vector<AudioFrame> p_volumes);