summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
authorEllen Poe <ellenhp@google.com>2021-08-27 21:51:03 -0700
committerEllen Poe <ellenhp@google.com>2021-09-07 09:44:39 -0700
commit0e3cab41ebac855fbf13b4c409e42d2661c5842b (patch)
treef2b0c9bd7ac718ecbf540af24208f43056650ba9 /scene
parentec70295c01839be4d4d2cf8c149ff32c65e75fd9 (diff)
Add polyphony to Audio Stream Player nodes
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/audio_stream_player_2d.cpp162
-rw-r--r--scene/2d/audio_stream_player_2d.h10
-rw-r--r--scene/3d/audio_stream_player_3d.cpp165
-rw-r--r--scene/3d/audio_stream_player_3d.h15
-rw-r--r--scene/audio/audio_stream_player.cpp137
-rw-r--r--scene/audio/audio_stream_player.h8
-rw-r--r--scene/resources/audio_stream_sample.cpp4
-rw-r--r--scene/resources/audio_stream_sample.h2
8 files changed, 310 insertions, 193 deletions
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index ea491e8b0e..9280e35c3f 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/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");
diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h
index 6428fbe017..5360fd4934 100644
--- a/scene/2d/audio_stream_player_2d.h
+++ b/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();
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index 16d5b9da3e..907c6cd03a 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/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");
diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h
index f915abad2b..abd5a841b2 100644
--- a/scene/3d/audio_stream_player_3d.h
+++ b/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();
diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp
index f7b7604fd5..0334ba667b 100644
--- a/scene/audio/audio_stream_player.cpp
+++ b/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"));
diff --git a/scene/audio/audio_stream_player.h b/scene/audio/audio_stream_player.h
index 76b6698c9d..7205fce204 100644
--- a/scene/audio/audio_stream_player.h
+++ b/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();
diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp
index 2ab9b7b5a4..d018103e64 100644
--- a/scene/resources/audio_stream_sample.cpp
+++ b/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) {
diff --git a/scene/resources/audio_stream_sample.h b/scene/resources/audio_stream_sample.h
index 8bf3d29123..24198e3c98 100644
--- a/scene/resources/audio_stream_sample.h
+++ b/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;