diff options
Diffstat (limited to 'scene/2d')
-rw-r--r-- | scene/2d/audio_stream_player_2d.cpp | 326 | ||||
-rw-r--r-- | scene/2d/audio_stream_player_2d.h | 26 |
2 files changed, 123 insertions, 229 deletions
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index 8a4d42fd1f..ea491e8b0e 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -33,125 +33,17 @@ #include "scene/2d/area_2d.h" #include "scene/main/window.h" -void AudioStreamPlayer2D::_mix_audio() { - if (!stream_playback.is_valid() || !active.is_set() || - (stream_paused && !stream_paused_fade_out)) { - return; - } - - if (setseek.get() >= 0.0) { - stream_playback->start(setseek.get()); - setseek.set(-1.0); //reset seek - } - - //get data - AudioFrame *buffer = mix_buffer.ptrw(); - int buffer_size = mix_buffer.size(); - - if (stream_paused_fade_out) { - // Short fadeout ramp - buffer_size = MIN(buffer_size, 128); - } - - stream_playback->mix(buffer, pitch_scale, buffer_size); - - //write all outputs - int oc = output_count.get(); - for (int i = 0; i < oc; i++) { - Output current = outputs[i]; - - //see if current output exists, to keep volume ramp - bool found = false; - for (int j = i; j < prev_output_count; j++) { - if (prev_outputs[j].viewport == current.viewport) { - if (j != i) { - SWAP(prev_outputs[j], prev_outputs[i]); - } - found = true; - break; - } - } - - if (!found) { - //create new if was not used before - if (prev_output_count < MAX_OUTPUTS) { - prev_outputs[prev_output_count] = prev_outputs[i]; //may be owned by another viewport - prev_output_count++; - } - prev_outputs[i] = current; - } - - //mix! - AudioFrame target_volume = stream_paused_fade_out ? AudioFrame(0.f, 0.f) : current.vol; - AudioFrame vol_prev = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : prev_outputs[i].vol; - AudioFrame vol_inc = (target_volume - vol_prev) / float(buffer_size); - AudioFrame vol = vol_prev; - - int cc = AudioServer::get_singleton()->get_channel_count(); - - if (cc == 1) { - if (!AudioServer::get_singleton()->thread_has_channel_mix_buffer(current.bus_index, 0)) { - continue; //may have been removed - } - - AudioFrame *target = AudioServer::get_singleton()->thread_get_channel_mix_buffer(current.bus_index, 0); - - for (int j = 0; j < buffer_size; j++) { - target[j] += buffer[j] * vol; - vol += vol_inc; - } - - } else { - AudioFrame *targets[4]; - bool valid = true; - - for (int k = 0; k < cc; k++) { - if (!AudioServer::get_singleton()->thread_has_channel_mix_buffer(current.bus_index, k)) { - valid = false; //may have been removed - break; - } - - targets[k] = AudioServer::get_singleton()->thread_get_channel_mix_buffer(current.bus_index, k); - } - - if (!valid) { - continue; - } - - for (int j = 0; j < buffer_size; j++) { - AudioFrame frame = buffer[j] * vol; - for (int k = 0; k < cc; k++) { - targets[k][j] += frame; - } - vol += vol_inc; - } - } - - prev_outputs[i] = current; - } - - prev_output_count = oc; - - //stream is no longer active, disable this. - if (!stream_playback->is_playing()) { - active.clear(); - } - - output_ready.clear(); - stream_paused_fade_in = false; - stream_paused_fade_out = false; -} - void AudioStreamPlayer2D::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { - AudioServer::get_singleton()->add_callback(_mix_audios, this); + AudioServer::get_singleton()->add_listener_changed_callback(_listener_changed_cb, this); if (autoplay && !Engine::get_singleton()->is_editor_hint()) { play(); } } if (p_what == NOTIFICATION_EXIT_TREE) { - AudioServer::get_singleton()->remove_callback(_mix_audios, this); + stop(); + AudioServer::get_singleton()->remove_listener_changed_callback(_listener_changed_cb, this); } if (p_what == NOTIFICATION_PAUSED) { @@ -168,109 +60,120 @@ void AudioStreamPlayer2D::_notification(int p_what) { if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { //update anything related to position first, if possible of course - if (!output_ready.is_set()) { - Ref<World2D> world_2d = get_world_2d(); - ERR_FAIL_COND(world_2d.is_null()); + if (!stream_playback.is_valid()) { + return; + } + 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); + } + } + + // 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")); + } + } +} - int new_output_count = 0; +StringName AudioStreamPlayer2D::_get_actual_bus() { + if (!stream_playback.is_valid()) { + return SNAME("Master"); + } - Vector2 global_pos = get_global_position(); + Vector2 global_pos = get_global_position(); - int bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus); + //check if any area is diverting sound into a bus + Ref<World2D> world_2d = get_world_2d(); + ERR_FAIL_COND_V(world_2d.is_null(), SNAME("Master")); - //check if any area is diverting sound into a bus + PhysicsDirectSpaceState2D *space_state = PhysicsServer2D::get_singleton()->space_get_direct_state(world_2d->get_space()); + PhysicsDirectSpaceState2D::ShapeResult sr[MAX_INTERSECT_AREAS]; - PhysicsDirectSpaceState2D *space_state = PhysicsServer2D::get_singleton()->space_get_direct_state(world_2d->get_space()); + int areas = space_state->intersect_point(global_pos, sr, MAX_INTERSECT_AREAS, Set<RID>(), area_mask, false, true); - PhysicsDirectSpaceState2D::ShapeResult sr[MAX_INTERSECT_AREAS]; + for (int i = 0; i < areas; i++) { + Area2D *area2d = Object::cast_to<Area2D>(sr[i].collider); + if (!area2d) { + continue; + } - int areas = space_state->intersect_point(global_pos, sr, MAX_INTERSECT_AREAS, Set<RID>(), area_mask, false, true); + if (!area2d->is_overriding_audio_bus()) { + continue; + } - for (int i = 0; i < areas; i++) { - Area2D *area2d = Object::cast_to<Area2D>(sr[i].collider); - if (!area2d) { - continue; - } + return area2d->get_audio_bus_name(); + } + return default_bus; +} - if (!area2d->is_overriding_audio_bus()) { - continue; - } +void AudioStreamPlayer2D::_update_panning() { + if (!stream_playback.is_valid()) { + return; + } - StringName bus_name = area2d->get_audio_bus_name(); - bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus_name); - break; - } + last_mix_count = AudioServer::get_singleton()->get_mix_count(); - const Set<Viewport *> viewports = world_2d->get_viewports(); + Ref<World2D> world_2d = get_world_2d(); + ERR_FAIL_COND(world_2d.is_null()); - for (Set<Viewport *>::Element *E = viewports.front(); E; E = E->next()) { - Viewport *vp = E->get(); - if (vp->is_audio_listener_2d()) { - //compute matrix to convert to screen - Transform2D to_screen = vp->get_global_canvas_transform() * vp->get_canvas_transform(); - Vector2 screen_size = vp->get_visible_rect().size; + Vector2 global_pos = get_global_position(); - //screen in global is used for attenuation - Vector2 screen_in_global = to_screen.affine_inverse().xform(screen_size * 0.5); + Set<Viewport *> viewports = world_2d->get_viewports(); + viewports.insert(get_viewport()); // TODO: This is a mediocre workaround for #50958. Remove when that bug is fixed! - float dist = global_pos.distance_to(screen_in_global); //distance to screen center + volume_vector.resize(4); + volume_vector.write[0] = AudioFrame(0, 0); + volume_vector.write[1] = AudioFrame(0, 0); + volume_vector.write[2] = AudioFrame(0, 0); + volume_vector.write[3] = AudioFrame(0, 0); - if (dist > max_distance) { - continue; //can't hear this sound in this viewport - } + for (Viewport *vp : viewports) { + if (!vp->is_audio_listener_2d()) { + continue; + } + //compute matrix to convert to screen + Transform2D to_screen = vp->get_global_canvas_transform() * vp->get_canvas_transform(); + Vector2 screen_size = vp->get_visible_rect().size; - float multiplier = Math::pow(1.0f - dist / max_distance, attenuation); - multiplier *= Math::db2linear(volume_db); //also apply player volume! + //screen in global is used for attenuation + Vector2 screen_in_global = to_screen.affine_inverse().xform(screen_size * 0.5); - //point in screen is used for panning - Vector2 point_in_screen = to_screen.xform(global_pos); + float dist = global_pos.distance_to(screen_in_global); //distance to screen center - float pan = CLAMP(point_in_screen.x / screen_size.width, 0.0, 1.0); + if (dist > max_distance) { + continue; //can't hear this sound in this viewport + } - float l = 1.0 - pan; - float r = pan; + float multiplier = Math::pow(1.0f - dist / max_distance, attenuation); + multiplier *= Math::db2linear(volume_db); //also apply player volume! - outputs[new_output_count].vol = AudioFrame(l, r) * multiplier; - outputs[new_output_count].bus_index = bus_index; - outputs[new_output_count].viewport = vp; //keep pointer only for reference - new_output_count++; - if (new_output_count == MAX_OUTPUTS) { - break; - } - } - } + //point in screen is used for panning + Vector2 point_in_screen = to_screen.xform(global_pos); - output_count.set(new_output_count); - output_ready.set(); - } + float pan = CLAMP(point_in_screen.x / screen_size.width, 0.0, 1.0); - //start playing if requested - if (setplay.get() >= 0.0) { - setseek.set(setplay.get()); - active.set(); - setplay.set(-1); - } + float l = 1.0 - pan; + float r = pan; - //stop playing if no longer active - if (!active.is_set()) { - set_physics_process_internal(false); - emit_signal(SNAME("finished")); - } + 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) { - AudioServer::get_singleton()->lock(); - - mix_buffer.resize(AudioServer::get_singleton()->thread_get_mix_buffer_size()); - if (stream_playback.is_valid()) { - stream_playback.unref(); - stream.unref(); - active.clear(); - setseek.set(-1); + stop(); } + stream_playback.unref(); + stream.unref(); if (p_stream.is_valid()) { stream_playback = p_stream->instance_playback(); if (stream_playback.is_valid()) { @@ -280,8 +183,6 @@ void AudioStreamPlayer2D::set_stream(Ref<AudioStream> p_stream) { } } - AudioServer::get_singleton()->unlock(); - if (p_stream.is_valid() && stream_playback.is_null()) { stream.unref(); } @@ -302,6 +203,9 @@ 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); + } } float AudioStreamPlayer2D::get_pitch_scale() const { @@ -309,27 +213,26 @@ float AudioStreamPlayer2D::get_pitch_scale() const { } void AudioStreamPlayer2D::play(float p_from_pos) { - if (!is_playing()) { - // Reset the prev_output_count if the stream is stopped - prev_output_count = 0; + stop(); + if (stream.is_valid()) { + stream_playback = stream->instance_playback(); } - if (stream_playback.is_valid()) { setplay.set(p_from_pos); - output_ready.clear(); set_physics_process_internal(true); } } void AudioStreamPlayer2D::seek(float p_seconds) { - if (stream_playback.is_valid()) { - setseek.set(p_seconds); + if (stream_playback.is_valid() && active.is_set()) { + 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); } @@ -337,7 +240,7 @@ void AudioStreamPlayer2D::stop() { bool AudioStreamPlayer2D::is_playing() const { if (stream_playback.is_valid()) { - return active.is_set() || setplay.get() >= 0; + return AudioServer::get_singleton()->is_playback_active(stream_playback); } return false; @@ -345,30 +248,23 @@ bool AudioStreamPlayer2D::is_playing() const { float AudioStreamPlayer2D::get_playback_position() { if (stream_playback.is_valid()) { - float ss = setseek.get(); - if (ss >= 0.0) { - return ss; - } - return stream_playback->get_playback_position(); + return AudioServer::get_singleton()->get_playback_position(stream_playback); } return 0; } void AudioStreamPlayer2D::set_bus(const StringName &p_bus) { - //if audio is active, must lock this - AudioServer::get_singleton()->lock(); - bus = p_bus; - AudioServer::get_singleton()->unlock(); + default_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) == bus) { - return bus; + if (AudioServer::get_singleton()->get_bus_name(i) == default_bus) { + return default_bus; } } - return "Master"; + return SNAME("Master"); } void AudioStreamPlayer2D::set_autoplay(bool p_enable) { @@ -388,7 +284,11 @@ void AudioStreamPlayer2D::_set_playing(bool p_enable) { } bool AudioStreamPlayer2D::_is_active() const { - return active.is_set(); + 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; } void AudioStreamPlayer2D::_validate_property(PropertyInfo &property) const { @@ -436,15 +336,17 @@ uint32_t AudioStreamPlayer2D::get_area_mask() const { } void AudioStreamPlayer2D::set_stream_paused(bool p_pause) { - if (p_pause != stream_paused) { - stream_paused = p_pause; - stream_paused_fade_in = !p_pause; - stream_paused_fade_out = 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); } } bool AudioStreamPlayer2D::get_stream_paused() const { - return stream_paused; + if (stream_playback.is_valid()) { + return AudioServer::get_singleton()->is_playback_paused(stream_playback); + } + return false; } Ref<AudioStreamPlayback> AudioStreamPlayer2D::get_stream_playback() { diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h index cf05a49b00..6428fbe017 100644 --- a/scene/2d/audio_stream_player_2d.h +++ b/scene/2d/audio_stream_player_2d.h @@ -51,38 +51,30 @@ private: Viewport *viewport = nullptr; //pointer only used for reference to previous mix }; - Output outputs[MAX_OUTPUTS]; - SafeNumeric<int> output_count; - SafeFlag output_ready; - - //these are used by audio thread to have a reference of previous volumes (for ramping volume and avoiding clicks) - Output prev_outputs[MAX_OUTPUTS]; - int prev_output_count = 0; - Ref<AudioStreamPlayback> stream_playback; Ref<AudioStream> stream; - Vector<AudioFrame> mix_buffer; - SafeNumeric<float> setseek{ -1.0 }; SafeFlag active; SafeNumeric<float> setplay{ -1.0 }; + Vector<AudioFrame> volume_vector; + + uint64_t last_mix_count = -1; + float volume_db = 0.0; float pitch_scale = 1.0; bool autoplay = false; - bool stream_paused = false; - bool stream_paused_fade_in = false; - bool stream_paused_fade_out = false; - StringName bus; - - void _mix_audio(); - static void _mix_audios(void *self) { reinterpret_cast<AudioStreamPlayer2D *>(self)->_mix_audio(); } + StringName default_bus = "Master"; void _set_playing(bool p_enable); bool _is_active() const; + StringName _get_actual_bus(); + void _update_panning(); void _bus_layout_changed(); + static void _listener_changed_cb(void *self) { reinterpret_cast<AudioStreamPlayer2D *>(self)->_update_panning(); } + uint32_t area_mask = 1; float max_distance = 2000.0; |