| 1 | /**************************************************************************/ |
| 2 | /* editor_audio_buses.cpp */ |
| 3 | /**************************************************************************/ |
| 4 | /* This file is part of: */ |
| 5 | /* GODOT ENGINE */ |
| 6 | /* https://godotengine.org */ |
| 7 | /**************************************************************************/ |
| 8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
| 9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
| 10 | /* */ |
| 11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
| 12 | /* a copy of this software and associated documentation files (the */ |
| 13 | /* "Software"), to deal in the Software without restriction, including */ |
| 14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
| 15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
| 16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
| 17 | /* the following conditions: */ |
| 18 | /* */ |
| 19 | /* The above copyright notice and this permission notice shall be */ |
| 20 | /* included in all copies or substantial portions of the Software. */ |
| 21 | /* */ |
| 22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
| 23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
| 24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
| 25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
| 26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
| 27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
| 28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
| 29 | /**************************************************************************/ |
| 30 | |
| 31 | #include "editor_audio_buses.h" |
| 32 | |
| 33 | #include "core/config/project_settings.h" |
| 34 | #include "core/input/input.h" |
| 35 | #include "core/io/resource_saver.h" |
| 36 | #include "core/os/keyboard.h" |
| 37 | #include "editor/editor_node.h" |
| 38 | #include "editor/editor_scale.h" |
| 39 | #include "editor/editor_settings.h" |
| 40 | #include "editor/editor_string_names.h" |
| 41 | #include "editor/editor_undo_redo_manager.h" |
| 42 | #include "editor/filesystem_dock.h" |
| 43 | #include "editor/gui/editor_file_dialog.h" |
| 44 | #include "scene/gui/separator.h" |
| 45 | #include "scene/resources/font.h" |
| 46 | #include "servers/audio_server.h" |
| 47 | |
| 48 | void EditorAudioBus::_update_visible_channels() { |
| 49 | int i = 0; |
| 50 | for (; i < cc; i++) { |
| 51 | if (!channel[i].vu_l->is_visible()) { |
| 52 | channel[i].vu_l->show(); |
| 53 | } |
| 54 | if (!channel[i].vu_r->is_visible()) { |
| 55 | channel[i].vu_r->show(); |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | for (; i < CHANNELS_MAX; i++) { |
| 60 | if (channel[i].vu_l->is_visible()) { |
| 61 | channel[i].vu_l->hide(); |
| 62 | } |
| 63 | if (channel[i].vu_r->is_visible()) { |
| 64 | channel[i].vu_r->hide(); |
| 65 | } |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | void EditorAudioBus::_notification(int p_what) { |
| 70 | switch (p_what) { |
| 71 | case NOTIFICATION_ENTER_TREE: |
| 72 | case NOTIFICATION_THEME_CHANGED: { |
| 73 | Ref<Texture2D> active_bus_texture = get_editor_theme_icon(SNAME("BusVuActive" )); |
| 74 | for (int i = 0; i < CHANNELS_MAX; i++) { |
| 75 | channel[i].vu_l->set_under_texture(active_bus_texture); |
| 76 | channel[i].vu_l->set_tint_under(Color(0.75, 0.75, 0.75)); |
| 77 | channel[i].vu_l->set_progress_texture(active_bus_texture); |
| 78 | |
| 79 | channel[i].vu_r->set_under_texture(active_bus_texture); |
| 80 | channel[i].vu_r->set_tint_under(Color(0.75, 0.75, 0.75)); |
| 81 | channel[i].vu_r->set_progress_texture(active_bus_texture); |
| 82 | channel[i].prev_active = true; |
| 83 | } |
| 84 | |
| 85 | disabled_vu = get_editor_theme_icon(SNAME("BusVuFrozen" )); |
| 86 | |
| 87 | Color solo_color = EditorSettings::get_singleton()->is_dark_theme() ? Color(1.0, 0.89, 0.22) : Color(1.0, 0.92, 0.44); |
| 88 | Color mute_color = EditorSettings::get_singleton()->is_dark_theme() ? Color(1.0, 0.16, 0.16) : Color(1.0, 0.44, 0.44); |
| 89 | Color bypass_color = EditorSettings::get_singleton()->is_dark_theme() ? Color(0.13, 0.8, 1.0) : Color(0.44, 0.87, 1.0); |
| 90 | |
| 91 | solo->set_icon(get_editor_theme_icon(SNAME("AudioBusSolo" ))); |
| 92 | solo->add_theme_color_override("icon_pressed_color" , solo_color); |
| 93 | mute->set_icon(get_editor_theme_icon(SNAME("AudioBusMute" ))); |
| 94 | mute->add_theme_color_override("icon_pressed_color" , mute_color); |
| 95 | bypass->set_icon(get_editor_theme_icon(SNAME("AudioBusBypass" ))); |
| 96 | bypass->add_theme_color_override("icon_pressed_color" , bypass_color); |
| 97 | |
| 98 | bus_options->set_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl" ))); |
| 99 | |
| 100 | audio_value_preview_label->add_theme_color_override("font_color" , get_theme_color(SNAME("font_color" ), SNAME("TooltipLabel" ))); |
| 101 | audio_value_preview_label->add_theme_color_override("font_shadow_color" , get_theme_color(SNAME("font_shadow_color" ), SNAME("TooltipLabel" ))); |
| 102 | audio_value_preview_box->add_theme_style_override("panel" , get_theme_stylebox(SNAME("panel" ), SNAME("TooltipPanel" ))); |
| 103 | |
| 104 | for (int i = 0; i < effect_options->get_item_count(); i++) { |
| 105 | String class_name = effect_options->get_item_metadata(i); |
| 106 | Ref<Texture> icon = EditorNode::get_singleton()->get_class_icon(class_name); |
| 107 | effect_options->set_item_icon(i, icon); |
| 108 | } |
| 109 | } break; |
| 110 | |
| 111 | case NOTIFICATION_READY: { |
| 112 | update_bus(); |
| 113 | set_process(true); |
| 114 | } break; |
| 115 | |
| 116 | case NOTIFICATION_DRAW: { |
| 117 | if (is_master) { |
| 118 | draw_style_box(get_theme_stylebox(SNAME("disabled" ), SNAME("Button" )), Rect2(Vector2(), get_size())); |
| 119 | } else if (has_focus()) { |
| 120 | draw_style_box(get_theme_stylebox(SNAME("focus" ), SNAME("Button" )), Rect2(Vector2(), get_size())); |
| 121 | } else { |
| 122 | draw_style_box(get_theme_stylebox(SNAME("panel" ), SNAME("TabContainer" )), Rect2(Vector2(), get_size())); |
| 123 | } |
| 124 | |
| 125 | if (get_index() != 0 && hovering_drop) { |
| 126 | Color accent = get_theme_color(SNAME("accent_color" ), EditorStringName(Editor)); |
| 127 | accent.a *= 0.7; |
| 128 | draw_rect(Rect2(Point2(), get_size()), accent, false); |
| 129 | } |
| 130 | } break; |
| 131 | |
| 132 | case NOTIFICATION_PROCESS: { |
| 133 | if (cc != AudioServer::get_singleton()->get_bus_channels(get_index())) { |
| 134 | cc = AudioServer::get_singleton()->get_bus_channels(get_index()); |
| 135 | _update_visible_channels(); |
| 136 | } |
| 137 | |
| 138 | for (int i = 0; i < cc; i++) { |
| 139 | float real_peak[2] = { -100, -100 }; |
| 140 | bool activity_found = false; |
| 141 | |
| 142 | if (AudioServer::get_singleton()->is_bus_channel_active(get_index(), i)) { |
| 143 | activity_found = true; |
| 144 | real_peak[0] = MAX(real_peak[0], AudioServer::get_singleton()->get_bus_peak_volume_left_db(get_index(), i)); |
| 145 | real_peak[1] = MAX(real_peak[1], AudioServer::get_singleton()->get_bus_peak_volume_right_db(get_index(), i)); |
| 146 | } |
| 147 | |
| 148 | if (real_peak[0] > channel[i].peak_l) { |
| 149 | channel[i].peak_l = real_peak[0]; |
| 150 | } else { |
| 151 | channel[i].peak_l -= get_process_delta_time() * 60.0; |
| 152 | } |
| 153 | |
| 154 | if (real_peak[1] > channel[i].peak_r) { |
| 155 | channel[i].peak_r = real_peak[1]; |
| 156 | } else { |
| 157 | channel[i].peak_r -= get_process_delta_time() * 60.0; |
| 158 | } |
| 159 | |
| 160 | channel[i].vu_l->set_value(channel[i].peak_l); |
| 161 | channel[i].vu_r->set_value(channel[i].peak_r); |
| 162 | |
| 163 | if (activity_found != channel[i].prev_active) { |
| 164 | if (activity_found) { |
| 165 | channel[i].vu_l->set_over_texture(Ref<Texture2D>()); |
| 166 | channel[i].vu_r->set_over_texture(Ref<Texture2D>()); |
| 167 | } else { |
| 168 | channel[i].vu_l->set_over_texture(disabled_vu); |
| 169 | channel[i].vu_r->set_over_texture(disabled_vu); |
| 170 | } |
| 171 | |
| 172 | channel[i].prev_active = activity_found; |
| 173 | } |
| 174 | } |
| 175 | } break; |
| 176 | |
| 177 | case NOTIFICATION_VISIBILITY_CHANGED: { |
| 178 | for (int i = 0; i < CHANNELS_MAX; i++) { |
| 179 | channel[i].peak_l = -100; |
| 180 | channel[i].peak_r = -100; |
| 181 | channel[i].prev_active = true; |
| 182 | } |
| 183 | |
| 184 | set_process(is_visible_in_tree()); |
| 185 | } break; |
| 186 | |
| 187 | case NOTIFICATION_MOUSE_EXIT: |
| 188 | case NOTIFICATION_DRAG_END: { |
| 189 | if (hovering_drop) { |
| 190 | hovering_drop = false; |
| 191 | queue_redraw(); |
| 192 | } |
| 193 | } break; |
| 194 | } |
| 195 | } |
| 196 | |
| 197 | void EditorAudioBus::update_send() { |
| 198 | send->clear(); |
| 199 | if (is_master) { |
| 200 | send->set_disabled(true); |
| 201 | send->set_text(TTR("Speakers" )); |
| 202 | } else { |
| 203 | send->set_disabled(false); |
| 204 | StringName current_send = AudioServer::get_singleton()->get_bus_send(get_index()); |
| 205 | int current_send_index = 0; //by default to master |
| 206 | |
| 207 | for (int i = 0; i < get_index(); i++) { |
| 208 | StringName send_name = AudioServer::get_singleton()->get_bus_name(i); |
| 209 | send->add_item(send_name); |
| 210 | if (send_name == current_send) { |
| 211 | current_send_index = i; |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | send->select(current_send_index); |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | void EditorAudioBus::update_bus() { |
| 220 | if (updating_bus) { |
| 221 | return; |
| 222 | } |
| 223 | |
| 224 | updating_bus = true; |
| 225 | |
| 226 | int index = get_index(); |
| 227 | |
| 228 | float db_value = AudioServer::get_singleton()->get_bus_volume_db(index); |
| 229 | slider->set_value(_scaled_db_to_normalized_volume(db_value)); |
| 230 | track_name->set_text(AudioServer::get_singleton()->get_bus_name(index)); |
| 231 | if (is_master) { |
| 232 | track_name->set_editable(false); |
| 233 | } |
| 234 | |
| 235 | solo->set_pressed(AudioServer::get_singleton()->is_bus_solo(index)); |
| 236 | mute->set_pressed(AudioServer::get_singleton()->is_bus_mute(index)); |
| 237 | bypass->set_pressed(AudioServer::get_singleton()->is_bus_bypassing_effects(index)); |
| 238 | // effects.. |
| 239 | effects->clear(); |
| 240 | |
| 241 | TreeItem *root = effects->create_item(); |
| 242 | for (int i = 0; i < AudioServer::get_singleton()->get_bus_effect_count(index); i++) { |
| 243 | Ref<AudioEffect> afx = AudioServer::get_singleton()->get_bus_effect(index, i); |
| 244 | |
| 245 | TreeItem *fx = effects->create_item(root); |
| 246 | fx->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); |
| 247 | fx->set_editable(0, true); |
| 248 | fx->set_checked(0, AudioServer::get_singleton()->is_bus_effect_enabled(index, i)); |
| 249 | fx->set_text(0, afx->get_name()); |
| 250 | fx->set_metadata(0, i); |
| 251 | } |
| 252 | |
| 253 | TreeItem *add = effects->create_item(root); |
| 254 | add->set_cell_mode(0, TreeItem::CELL_MODE_CUSTOM); |
| 255 | add->set_editable(0, true); |
| 256 | add->set_selectable(0, false); |
| 257 | add->set_text(0, TTR("Add Effect" )); |
| 258 | |
| 259 | update_send(); |
| 260 | |
| 261 | updating_bus = false; |
| 262 | } |
| 263 | |
| 264 | void EditorAudioBus::_name_changed(const String &p_new_name) { |
| 265 | if (updating_bus) { |
| 266 | return; |
| 267 | } |
| 268 | updating_bus = true; |
| 269 | track_name->release_focus(); |
| 270 | |
| 271 | if (p_new_name == AudioServer::get_singleton()->get_bus_name(get_index())) { |
| 272 | updating_bus = false; |
| 273 | return; |
| 274 | } |
| 275 | |
| 276 | String attempt = p_new_name; |
| 277 | int attempts = 1; |
| 278 | |
| 279 | while (true) { |
| 280 | bool name_free = true; |
| 281 | for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { |
| 282 | if (AudioServer::get_singleton()->get_bus_name(i) == attempt) { |
| 283 | name_free = false; |
| 284 | break; |
| 285 | } |
| 286 | } |
| 287 | |
| 288 | if (name_free) { |
| 289 | break; |
| 290 | } |
| 291 | |
| 292 | attempts++; |
| 293 | attempt = p_new_name + " " + itos(attempts); |
| 294 | } |
| 295 | |
| 296 | EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); |
| 297 | |
| 298 | StringName current = AudioServer::get_singleton()->get_bus_name(get_index()); |
| 299 | |
| 300 | ur->create_action(TTR("Rename Audio Bus" )); |
| 301 | ur->add_do_method(buses, "_set_renaming_buses" , true); |
| 302 | ur->add_undo_method(buses, "_set_renaming_buses" , true); |
| 303 | |
| 304 | ur->add_do_method(AudioServer::get_singleton(), "set_bus_name" , get_index(), attempt); |
| 305 | ur->add_undo_method(AudioServer::get_singleton(), "set_bus_name" , get_index(), current); |
| 306 | |
| 307 | for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { |
| 308 | if (AudioServer::get_singleton()->get_bus_send(i) == current) { |
| 309 | ur->add_do_method(AudioServer::get_singleton(), "set_bus_send" , i, attempt); |
| 310 | ur->add_undo_method(AudioServer::get_singleton(), "set_bus_send" , i, current); |
| 311 | } |
| 312 | } |
| 313 | |
| 314 | ur->add_do_method(buses, "_update_bus" , get_index()); |
| 315 | ur->add_undo_method(buses, "_update_bus" , get_index()); |
| 316 | |
| 317 | ur->add_do_method(buses, "_update_sends" ); |
| 318 | ur->add_undo_method(buses, "_update_sends" ); |
| 319 | |
| 320 | ur->add_do_method(buses, "_set_renaming_buses" , false); |
| 321 | ur->add_undo_method(buses, "_set_renaming_buses" , false); |
| 322 | ur->commit_action(); |
| 323 | |
| 324 | updating_bus = false; |
| 325 | } |
| 326 | |
| 327 | void EditorAudioBus::_volume_changed(float p_normalized) { |
| 328 | if (updating_bus) { |
| 329 | return; |
| 330 | } |
| 331 | |
| 332 | updating_bus = true; |
| 333 | |
| 334 | const float p_db = this->_normalized_volume_to_scaled_db(p_normalized); |
| 335 | |
| 336 | if (Input::get_singleton()->is_key_pressed(Key::CTRL)) { |
| 337 | // Snap the value when holding Ctrl for easier editing. |
| 338 | // To do so, it needs to be converted back to normalized volume (as the slider uses that unit). |
| 339 | slider->set_value(_scaled_db_to_normalized_volume(Math::round(p_db))); |
| 340 | } |
| 341 | |
| 342 | EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); |
| 343 | ur->create_action(TTR("Change Audio Bus Volume" ), UndoRedo::MERGE_ENDS); |
| 344 | ur->add_do_method(AudioServer::get_singleton(), "set_bus_volume_db" , get_index(), p_db); |
| 345 | ur->add_undo_method(AudioServer::get_singleton(), "set_bus_volume_db" , get_index(), AudioServer::get_singleton()->get_bus_volume_db(get_index())); |
| 346 | ur->add_do_method(buses, "_update_bus" , get_index()); |
| 347 | ur->add_undo_method(buses, "_update_bus" , get_index()); |
| 348 | ur->commit_action(); |
| 349 | |
| 350 | updating_bus = false; |
| 351 | } |
| 352 | |
| 353 | float EditorAudioBus::_normalized_volume_to_scaled_db(float normalized) { |
| 354 | /* There are three different formulas for the conversion from normalized |
| 355 | * values to relative decibal values. |
| 356 | * One formula is an exponential graph which intends to counteract |
| 357 | * the logarithmic nature of human hearing. This is an approximation |
| 358 | * of the behavior of a 'logarithmic potentiometer' found on most |
| 359 | * musical instruments and also emulated in popular software. |
| 360 | * The other two equations are hand-tuned linear tapers that intend to |
| 361 | * try to ease the exponential equation in areas where it makes sense.*/ |
| 362 | |
| 363 | if (normalized > 0.6f) { |
| 364 | return 22.22f * normalized - 16.2f; |
| 365 | } else if (normalized < 0.05f) { |
| 366 | return 830.72 * normalized - 80.0f; |
| 367 | } else { |
| 368 | return 45.0f * Math::pow(normalized - 1.0, 3); |
| 369 | } |
| 370 | } |
| 371 | |
| 372 | float EditorAudioBus::_scaled_db_to_normalized_volume(float db) { |
| 373 | /* Inversion of equations found in _normalized_volume_to_scaled_db. |
| 374 | * IMPORTANT: If one function changes, the other much change to reflect it. */ |
| 375 | if (db > -2.88) { |
| 376 | return (db + 16.2f) / 22.22f; |
| 377 | } else if (db < -38.602f) { |
| 378 | return (db + 80.00f) / 830.72f; |
| 379 | } else { |
| 380 | if (db < 0.0) { |
| 381 | /* To accommodate for NaN on negative numbers for root, we will mirror the |
| 382 | * results of the positive db range in order to get the desired numerical |
| 383 | * value on the negative side. */ |
| 384 | float positive_x = Math::pow(Math::abs(db) / 45.0f, 1.0f / 3.0f) + 1.0f; |
| 385 | Vector2 translation = Vector2(1.0f, 0.0f) - Vector2(positive_x, Math::abs(db)); |
| 386 | Vector2 reflected_position = Vector2(1.0, 0.0f) + translation; |
| 387 | return reflected_position.x; |
| 388 | } else { |
| 389 | return Math::pow(db / 45.0f, 1.0f / 3.0f) + 1.0f; |
| 390 | } |
| 391 | } |
| 392 | } |
| 393 | |
| 394 | void EditorAudioBus::_show_value(float slider_value) { |
| 395 | float db; |
| 396 | if (Input::get_singleton()->is_key_pressed(Key::CTRL)) { |
| 397 | // Display the correct (snapped) value when holding Ctrl |
| 398 | db = Math::round(_normalized_volume_to_scaled_db(slider_value)); |
| 399 | } else { |
| 400 | db = _normalized_volume_to_scaled_db(slider_value); |
| 401 | } |
| 402 | |
| 403 | String text; |
| 404 | if (Math::is_zero_approx(Math::snapped(db, 0.1))) { |
| 405 | // Prevent displaying `-0.0 dB` and show ` 0.0 dB` instead. |
| 406 | // The leading space makes the text visually line up with its positive/negative counterparts. |
| 407 | text = " 0.0 dB" ; |
| 408 | } else { |
| 409 | // Show an explicit `+` sign if positive. |
| 410 | text = vformat("%+.1f dB" , db); |
| 411 | } |
| 412 | |
| 413 | // Also set the preview text as a standard Control tooltip. |
| 414 | // This way, it can be seen when the slider is merely hovered (instead of dragged). |
| 415 | slider->set_tooltip_text(text); |
| 416 | audio_value_preview_label->set_text(text); |
| 417 | const Vector2 slider_size = slider->get_size(); |
| 418 | const Vector2 slider_position = slider->get_global_position(); |
| 419 | const float vert_padding = 10.0f; |
| 420 | const Vector2 box_position = Vector2(slider_size.x, (slider_size.y - vert_padding) * (1.0f - slider->get_value()) - vert_padding); |
| 421 | audio_value_preview_box->set_position(slider_position + box_position); |
| 422 | audio_value_preview_box->set_size(audio_value_preview_label->get_size()); |
| 423 | if (slider->has_focus() && !audio_value_preview_box->is_visible()) { |
| 424 | audio_value_preview_box->show(); |
| 425 | } |
| 426 | preview_timer->start(); |
| 427 | } |
| 428 | |
| 429 | void EditorAudioBus::_hide_value_preview() { |
| 430 | audio_value_preview_box->hide(); |
| 431 | } |
| 432 | |
| 433 | void EditorAudioBus::_solo_toggled() { |
| 434 | updating_bus = true; |
| 435 | |
| 436 | EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); |
| 437 | ur->create_action(TTR("Toggle Audio Bus Solo" )); |
| 438 | ur->add_do_method(AudioServer::get_singleton(), "set_bus_solo" , get_index(), solo->is_pressed()); |
| 439 | ur->add_undo_method(AudioServer::get_singleton(), "set_bus_solo" , get_index(), AudioServer::get_singleton()->is_bus_solo(get_index())); |
| 440 | ur->add_do_method(buses, "_update_bus" , get_index()); |
| 441 | ur->add_undo_method(buses, "_update_bus" , get_index()); |
| 442 | ur->commit_action(); |
| 443 | |
| 444 | updating_bus = false; |
| 445 | } |
| 446 | |
| 447 | void EditorAudioBus::_mute_toggled() { |
| 448 | updating_bus = true; |
| 449 | |
| 450 | EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); |
| 451 | ur->create_action(TTR("Toggle Audio Bus Mute" )); |
| 452 | ur->add_do_method(AudioServer::get_singleton(), "set_bus_mute" , get_index(), mute->is_pressed()); |
| 453 | ur->add_undo_method(AudioServer::get_singleton(), "set_bus_mute" , get_index(), AudioServer::get_singleton()->is_bus_mute(get_index())); |
| 454 | ur->add_do_method(buses, "_update_bus" , get_index()); |
| 455 | ur->add_undo_method(buses, "_update_bus" , get_index()); |
| 456 | ur->commit_action(); |
| 457 | |
| 458 | updating_bus = false; |
| 459 | } |
| 460 | |
| 461 | void EditorAudioBus::_bypass_toggled() { |
| 462 | updating_bus = true; |
| 463 | |
| 464 | EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); |
| 465 | ur->create_action(TTR("Toggle Audio Bus Bypass Effects" )); |
| 466 | ur->add_do_method(AudioServer::get_singleton(), "set_bus_bypass_effects" , get_index(), bypass->is_pressed()); |
| 467 | ur->add_undo_method(AudioServer::get_singleton(), "set_bus_bypass_effects" , get_index(), AudioServer::get_singleton()->is_bus_bypassing_effects(get_index())); |
| 468 | ur->add_do_method(buses, "_update_bus" , get_index()); |
| 469 | ur->add_undo_method(buses, "_update_bus" , get_index()); |
| 470 | ur->commit_action(); |
| 471 | |
| 472 | updating_bus = false; |
| 473 | } |
| 474 | |
| 475 | void EditorAudioBus::_send_selected(int p_which) { |
| 476 | updating_bus = true; |
| 477 | |
| 478 | EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); |
| 479 | ur->create_action(TTR("Select Audio Bus Send" )); |
| 480 | ur->add_do_method(AudioServer::get_singleton(), "set_bus_send" , get_index(), send->get_item_text(p_which)); |
| 481 | ur->add_undo_method(AudioServer::get_singleton(), "set_bus_send" , get_index(), AudioServer::get_singleton()->get_bus_send(get_index())); |
| 482 | ur->add_do_method(buses, "_update_bus" , get_index()); |
| 483 | ur->add_undo_method(buses, "_update_bus" , get_index()); |
| 484 | ur->commit_action(); |
| 485 | |
| 486 | updating_bus = false; |
| 487 | } |
| 488 | |
| 489 | void EditorAudioBus::_effect_selected() { |
| 490 | TreeItem *effect = effects->get_selected(); |
| 491 | if (!effect) { |
| 492 | return; |
| 493 | } |
| 494 | updating_bus = true; |
| 495 | |
| 496 | if (effect->get_metadata(0) != Variant()) { |
| 497 | int index = effect->get_metadata(0); |
| 498 | Ref<AudioEffect> effect2 = AudioServer::get_singleton()->get_bus_effect(get_index(), index); |
| 499 | if (effect2.is_valid()) { |
| 500 | EditorNode::get_singleton()->push_item(effect2.ptr()); |
| 501 | } |
| 502 | } |
| 503 | |
| 504 | updating_bus = false; |
| 505 | } |
| 506 | |
| 507 | void EditorAudioBus::_effect_edited() { |
| 508 | if (updating_bus) { |
| 509 | return; |
| 510 | } |
| 511 | |
| 512 | TreeItem *effect = effects->get_edited(); |
| 513 | if (!effect) { |
| 514 | return; |
| 515 | } |
| 516 | |
| 517 | if (effect->get_metadata(0) == Variant()) { |
| 518 | Rect2 area = effects->get_item_rect(effect); |
| 519 | |
| 520 | effect_options->set_position(effects->get_screen_position() + area.position + Vector2(0, area.size.y)); |
| 521 | effect_options->reset_size(); |
| 522 | effect_options->popup(); |
| 523 | //add effect |
| 524 | } else { |
| 525 | int index = effect->get_metadata(0); |
| 526 | updating_bus = true; |
| 527 | |
| 528 | EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); |
| 529 | ur->create_action(TTR("Select Audio Bus Send" )); |
| 530 | ur->add_do_method(AudioServer::get_singleton(), "set_bus_effect_enabled" , get_index(), index, effect->is_checked(0)); |
| 531 | ur->add_undo_method(AudioServer::get_singleton(), "set_bus_effect_enabled" , get_index(), index, AudioServer::get_singleton()->is_bus_effect_enabled(get_index(), index)); |
| 532 | ur->add_do_method(buses, "_update_bus" , get_index()); |
| 533 | ur->add_undo_method(buses, "_update_bus" , get_index()); |
| 534 | ur->commit_action(); |
| 535 | |
| 536 | updating_bus = false; |
| 537 | } |
| 538 | } |
| 539 | |
| 540 | void EditorAudioBus::_effect_add(int p_which) { |
| 541 | if (updating_bus) { |
| 542 | return; |
| 543 | } |
| 544 | |
| 545 | StringName name = effect_options->get_item_metadata(p_which); |
| 546 | |
| 547 | Object *fx = ClassDB::instantiate(name); |
| 548 | ERR_FAIL_NULL(fx); |
| 549 | AudioEffect *afx = Object::cast_to<AudioEffect>(fx); |
| 550 | ERR_FAIL_NULL(afx); |
| 551 | Ref<AudioEffect> afxr = Ref<AudioEffect>(afx); |
| 552 | |
| 553 | afxr->set_name(effect_options->get_item_text(p_which)); |
| 554 | |
| 555 | EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); |
| 556 | ur->create_action(TTR("Add Audio Bus Effect" )); |
| 557 | ur->add_do_method(AudioServer::get_singleton(), "add_bus_effect" , get_index(), afxr, -1); |
| 558 | ur->add_undo_method(AudioServer::get_singleton(), "remove_bus_effect" , get_index(), AudioServer::get_singleton()->get_bus_effect_count(get_index())); |
| 559 | ur->add_do_method(buses, "_update_bus" , get_index()); |
| 560 | ur->add_undo_method(buses, "_update_bus" , get_index()); |
| 561 | ur->commit_action(); |
| 562 | } |
| 563 | |
| 564 | void EditorAudioBus::gui_input(const Ref<InputEvent> &p_event) { |
| 565 | ERR_FAIL_COND(p_event.is_null()); |
| 566 | |
| 567 | Ref<InputEventMouseButton> mb = p_event; |
| 568 | if (mb.is_valid() && mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) { |
| 569 | bus_popup->set_position(get_screen_position() + mb->get_position()); |
| 570 | bus_popup->reset_size(); |
| 571 | bus_popup->popup(); |
| 572 | } |
| 573 | } |
| 574 | |
| 575 | void EditorAudioBus::_effects_gui_input(Ref<InputEvent> p_event) { |
| 576 | Ref<InputEventKey> k = p_event; |
| 577 | if (k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_keycode() == Key::KEY_DELETE) { |
| 578 | TreeItem *current_effect = effects->get_selected(); |
| 579 | if (current_effect && current_effect->get_metadata(0).get_type() == Variant::INT) { |
| 580 | _delete_effect_pressed(0); |
| 581 | accept_event(); |
| 582 | } |
| 583 | } |
| 584 | } |
| 585 | |
| 586 | void EditorAudioBus::(int p_option) { |
| 587 | if (p_option == 2) { |
| 588 | // Reset volume |
| 589 | emit_signal(SNAME("vol_reset_request" )); |
| 590 | } else if (p_option == 1) { |
| 591 | emit_signal(SNAME("delete_request" )); |
| 592 | } else if (p_option == 0) { |
| 593 | //duplicate |
| 594 | emit_signal(SNAME("duplicate_request" ), get_index()); |
| 595 | } |
| 596 | } |
| 597 | |
| 598 | Variant EditorAudioBus::get_drag_data(const Point2 &p_point) { |
| 599 | if (get_index() == 0) { |
| 600 | return Variant(); |
| 601 | } |
| 602 | |
| 603 | Control *c = memnew(Control); |
| 604 | Panel *p = memnew(Panel); |
| 605 | c->add_child(p); |
| 606 | p->set_modulate(Color(1, 1, 1, 0.7)); |
| 607 | p->add_theme_style_override("panel" , get_theme_stylebox(SNAME("focus" ), SNAME("Button" ))); |
| 608 | p->set_size(get_size()); |
| 609 | p->set_position(-p_point); |
| 610 | set_drag_preview(c); |
| 611 | Dictionary d; |
| 612 | d["type" ] = "move_audio_bus" ; |
| 613 | d["index" ] = get_index(); |
| 614 | |
| 615 | if (get_index() < AudioServer::get_singleton()->get_bus_count() - 1) { |
| 616 | emit_signal(SNAME("drop_end_request" )); |
| 617 | } |
| 618 | |
| 619 | return d; |
| 620 | } |
| 621 | |
| 622 | bool EditorAudioBus::can_drop_data(const Point2 &p_point, const Variant &p_data) const { |
| 623 | if (get_index() == 0) { |
| 624 | return false; |
| 625 | } |
| 626 | |
| 627 | Dictionary d = p_data; |
| 628 | if (d.has("type" ) && String(d["type" ]) == "move_audio_bus" && (int)d["index" ] != get_index()) { |
| 629 | hovering_drop = true; |
| 630 | return true; |
| 631 | } |
| 632 | |
| 633 | return false; |
| 634 | } |
| 635 | |
| 636 | void EditorAudioBus::drop_data(const Point2 &p_point, const Variant &p_data) { |
| 637 | Dictionary d = p_data; |
| 638 | emit_signal(SNAME("dropped" ), d["index" ], get_index()); |
| 639 | } |
| 640 | |
| 641 | Variant EditorAudioBus::get_drag_data_fw(const Point2 &p_point, Control *p_from) { |
| 642 | TreeItem *item = effects->get_item_at_position(p_point); |
| 643 | if (!item) { |
| 644 | return Variant(); |
| 645 | } |
| 646 | |
| 647 | Variant md = item->get_metadata(0); |
| 648 | if (md.get_type() == Variant::INT) { |
| 649 | Dictionary fxd; |
| 650 | fxd["type" ] = "audio_bus_effect" ; |
| 651 | fxd["bus" ] = get_index(); |
| 652 | fxd["effect" ] = md; |
| 653 | |
| 654 | Label *l = memnew(Label); |
| 655 | l->set_text(item->get_text(0)); |
| 656 | effects->set_drag_preview(l); |
| 657 | |
| 658 | return fxd; |
| 659 | } |
| 660 | |
| 661 | return Variant(); |
| 662 | } |
| 663 | |
| 664 | bool EditorAudioBus::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { |
| 665 | Dictionary d = p_data; |
| 666 | if (!d.has("type" ) || String(d["type" ]) != "audio_bus_effect" ) { |
| 667 | return false; |
| 668 | } |
| 669 | |
| 670 | TreeItem *item = effects->get_item_at_position(p_point); |
| 671 | if (!item) { |
| 672 | return false; |
| 673 | } |
| 674 | |
| 675 | effects->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN); |
| 676 | |
| 677 | return true; |
| 678 | } |
| 679 | |
| 680 | void EditorAudioBus::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) { |
| 681 | Dictionary d = p_data; |
| 682 | |
| 683 | TreeItem *item = effects->get_item_at_position(p_point); |
| 684 | if (!item) { |
| 685 | return; |
| 686 | } |
| 687 | int pos = effects->get_drop_section_at_position(p_point); |
| 688 | Variant md = item->get_metadata(0); |
| 689 | |
| 690 | int paste_at; |
| 691 | int bus = d["bus" ]; |
| 692 | int effect = d["effect" ]; |
| 693 | |
| 694 | if (md.get_type() == Variant::INT) { |
| 695 | paste_at = md; |
| 696 | if (pos > 0) { |
| 697 | paste_at++; |
| 698 | } |
| 699 | |
| 700 | if (bus == get_index() && paste_at > effect) { |
| 701 | paste_at--; |
| 702 | } |
| 703 | } else { |
| 704 | paste_at = -1; |
| 705 | } |
| 706 | |
| 707 | bool enabled = AudioServer::get_singleton()->is_bus_effect_enabled(bus, effect); |
| 708 | |
| 709 | EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); |
| 710 | ur->create_action(TTR("Move Bus Effect" )); |
| 711 | ur->add_do_method(AudioServer::get_singleton(), "remove_bus_effect" , bus, effect); |
| 712 | ur->add_do_method(AudioServer::get_singleton(), "add_bus_effect" , get_index(), AudioServer::get_singleton()->get_bus_effect(bus, effect), paste_at); |
| 713 | |
| 714 | if (paste_at == -1) { |
| 715 | paste_at = AudioServer::get_singleton()->get_bus_effect_count(get_index()); |
| 716 | if (bus == get_index()) { |
| 717 | paste_at--; |
| 718 | } |
| 719 | } |
| 720 | if (!enabled) { |
| 721 | ur->add_do_method(AudioServer::get_singleton(), "set_bus_effect_enabled" , get_index(), paste_at, false); |
| 722 | } |
| 723 | |
| 724 | ur->add_undo_method(AudioServer::get_singleton(), "remove_bus_effect" , get_index(), paste_at); |
| 725 | ur->add_undo_method(AudioServer::get_singleton(), "add_bus_effect" , bus, AudioServer::get_singleton()->get_bus_effect(bus, effect), effect); |
| 726 | if (!enabled) { |
| 727 | ur->add_undo_method(AudioServer::get_singleton(), "set_bus_effect_enabled" , bus, effect, false); |
| 728 | } |
| 729 | |
| 730 | ur->add_do_method(buses, "_update_bus" , get_index()); |
| 731 | ur->add_undo_method(buses, "_update_bus" , get_index()); |
| 732 | if (get_index() != bus) { |
| 733 | ur->add_do_method(buses, "_update_bus" , bus); |
| 734 | ur->add_undo_method(buses, "_update_bus" , bus); |
| 735 | } |
| 736 | ur->commit_action(); |
| 737 | } |
| 738 | |
| 739 | void EditorAudioBus::_delete_effect_pressed(int p_option) { |
| 740 | TreeItem *item = effects->get_selected(); |
| 741 | if (!item) { |
| 742 | return; |
| 743 | } |
| 744 | |
| 745 | if (item->get_metadata(0).get_type() != Variant::INT) { |
| 746 | return; |
| 747 | } |
| 748 | |
| 749 | int index = item->get_metadata(0); |
| 750 | |
| 751 | EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); |
| 752 | ur->create_action(TTR("Delete Bus Effect" )); |
| 753 | ur->add_do_method(AudioServer::get_singleton(), "remove_bus_effect" , get_index(), index); |
| 754 | ur->add_undo_method(AudioServer::get_singleton(), "add_bus_effect" , get_index(), AudioServer::get_singleton()->get_bus_effect(get_index(), index), index); |
| 755 | ur->add_undo_method(AudioServer::get_singleton(), "set_bus_effect_enabled" , get_index(), index, AudioServer::get_singleton()->is_bus_effect_enabled(get_index(), index)); |
| 756 | ur->add_do_method(buses, "_update_bus" , get_index()); |
| 757 | ur->add_undo_method(buses, "_update_bus" , get_index()); |
| 758 | ur->commit_action(); |
| 759 | } |
| 760 | |
| 761 | void EditorAudioBus::_effect_rmb(const Vector2 &p_pos, MouseButton p_button) { |
| 762 | if (p_button != MouseButton::RIGHT) { |
| 763 | return; |
| 764 | } |
| 765 | |
| 766 | TreeItem *item = effects->get_selected(); |
| 767 | if (!item) { |
| 768 | return; |
| 769 | } |
| 770 | |
| 771 | if (item->get_metadata(0).get_type() != Variant::INT) { |
| 772 | return; |
| 773 | } |
| 774 | |
| 775 | delete_effect_popup->set_position(get_screen_position() + get_local_mouse_position()); |
| 776 | delete_effect_popup->reset_size(); |
| 777 | delete_effect_popup->popup(); |
| 778 | } |
| 779 | |
| 780 | void EditorAudioBus::_bind_methods() { |
| 781 | ClassDB::bind_method("update_bus" , &EditorAudioBus::update_bus); |
| 782 | ClassDB::bind_method("update_send" , &EditorAudioBus::update_send); |
| 783 | |
| 784 | ADD_SIGNAL(MethodInfo("duplicate_request" )); |
| 785 | ADD_SIGNAL(MethodInfo("delete_request" )); |
| 786 | ADD_SIGNAL(MethodInfo("vol_reset_request" )); |
| 787 | ADD_SIGNAL(MethodInfo("drop_end_request" )); |
| 788 | ADD_SIGNAL(MethodInfo("dropped" )); |
| 789 | } |
| 790 | |
| 791 | EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) { |
| 792 | buses = p_buses; |
| 793 | is_master = p_is_master; |
| 794 | |
| 795 | set_tooltip_text(TTR("Drag & drop to rearrange." )); |
| 796 | |
| 797 | VBoxContainer *vb = memnew(VBoxContainer); |
| 798 | add_child(vb); |
| 799 | |
| 800 | set_v_size_flags(SIZE_EXPAND_FILL); |
| 801 | |
| 802 | track_name = memnew(LineEdit); |
| 803 | track_name->connect("text_submitted" , callable_mp(this, &EditorAudioBus::_name_changed)); |
| 804 | track_name->connect("focus_exited" , callable_mp(this, &EditorAudioBus::_name_focus_exit)); |
| 805 | vb->add_child(track_name); |
| 806 | |
| 807 | HBoxContainer *hbc = memnew(HBoxContainer); |
| 808 | vb->add_child(hbc); |
| 809 | solo = memnew(Button); |
| 810 | solo->set_flat(true); |
| 811 | solo->set_toggle_mode(true); |
| 812 | solo->set_tooltip_text(TTR("Solo" )); |
| 813 | solo->set_focus_mode(FOCUS_NONE); |
| 814 | solo->connect("pressed" , callable_mp(this, &EditorAudioBus::_solo_toggled)); |
| 815 | hbc->add_child(solo); |
| 816 | mute = memnew(Button); |
| 817 | mute->set_flat(true); |
| 818 | mute->set_toggle_mode(true); |
| 819 | mute->set_tooltip_text(TTR("Mute" )); |
| 820 | mute->set_focus_mode(FOCUS_NONE); |
| 821 | mute->connect("pressed" , callable_mp(this, &EditorAudioBus::_mute_toggled)); |
| 822 | hbc->add_child(mute); |
| 823 | bypass = memnew(Button); |
| 824 | bypass->set_flat(true); |
| 825 | bypass->set_toggle_mode(true); |
| 826 | bypass->set_tooltip_text(TTR("Bypass" )); |
| 827 | bypass->set_focus_mode(FOCUS_NONE); |
| 828 | bypass->connect("pressed" , callable_mp(this, &EditorAudioBus::_bypass_toggled)); |
| 829 | hbc->add_child(bypass); |
| 830 | hbc->add_spacer(); |
| 831 | |
| 832 | Ref<StyleBoxEmpty> sbempty = memnew(StyleBoxEmpty); |
| 833 | for (int i = 0; i < hbc->get_child_count(); i++) { |
| 834 | Control *child = Object::cast_to<Control>(hbc->get_child(i)); |
| 835 | child->add_theme_style_override("normal" , sbempty); |
| 836 | child->add_theme_style_override("hover" , sbempty); |
| 837 | child->add_theme_style_override("focus" , sbempty); |
| 838 | child->add_theme_style_override("pressed" , sbempty); |
| 839 | } |
| 840 | |
| 841 | HSeparator *separator = memnew(HSeparator); |
| 842 | separator->set_mouse_filter(MOUSE_FILTER_PASS); |
| 843 | vb->add_child(separator); |
| 844 | |
| 845 | HBoxContainer *hb = memnew(HBoxContainer); |
| 846 | vb->add_child(hb); |
| 847 | slider = memnew(VSlider); |
| 848 | slider->set_min(0.0); |
| 849 | slider->set_max(1.0); |
| 850 | slider->set_step(0.0001); |
| 851 | slider->set_clip_contents(false); |
| 852 | |
| 853 | audio_value_preview_box = memnew(Panel); |
| 854 | slider->add_child(audio_value_preview_box); |
| 855 | audio_value_preview_box->set_as_top_level(true); |
| 856 | audio_value_preview_box->set_mouse_filter(MOUSE_FILTER_PASS); |
| 857 | audio_value_preview_box->hide(); |
| 858 | |
| 859 | HBoxContainer *audioprev_hbc = memnew(HBoxContainer); |
| 860 | audioprev_hbc->set_v_size_flags(SIZE_EXPAND_FILL); |
| 861 | audioprev_hbc->set_h_size_flags(SIZE_EXPAND_FILL); |
| 862 | audio_value_preview_box->add_child(audioprev_hbc); |
| 863 | |
| 864 | audio_value_preview_label = memnew(Label); |
| 865 | audio_value_preview_label->set_v_size_flags(SIZE_EXPAND_FILL); |
| 866 | audio_value_preview_label->set_h_size_flags(SIZE_EXPAND_FILL); |
| 867 | audio_value_preview_label->set_mouse_filter(MOUSE_FILTER_PASS); |
| 868 | audioprev_hbc->add_child(audio_value_preview_label); |
| 869 | |
| 870 | preview_timer = memnew(Timer); |
| 871 | preview_timer->set_wait_time(0.8f); |
| 872 | preview_timer->set_one_shot(true); |
| 873 | add_child(preview_timer); |
| 874 | |
| 875 | slider->connect("value_changed" , callable_mp(this, &EditorAudioBus::_volume_changed)); |
| 876 | slider->connect("value_changed" , callable_mp(this, &EditorAudioBus::_show_value)); |
| 877 | preview_timer->connect("timeout" , callable_mp(this, &EditorAudioBus::_hide_value_preview)); |
| 878 | hb->add_child(slider); |
| 879 | |
| 880 | cc = 0; |
| 881 | for (int i = 0; i < CHANNELS_MAX; i++) { |
| 882 | channel[i].vu_l = memnew(TextureProgressBar); |
| 883 | channel[i].vu_l->set_fill_mode(TextureProgressBar::FILL_BOTTOM_TO_TOP); |
| 884 | hb->add_child(channel[i].vu_l); |
| 885 | channel[i].vu_l->set_min(-80); |
| 886 | channel[i].vu_l->set_max(24); |
| 887 | channel[i].vu_l->set_step(0.1); |
| 888 | |
| 889 | channel[i].vu_r = memnew(TextureProgressBar); |
| 890 | channel[i].vu_r->set_fill_mode(TextureProgressBar::FILL_BOTTOM_TO_TOP); |
| 891 | hb->add_child(channel[i].vu_r); |
| 892 | channel[i].vu_r->set_min(-80); |
| 893 | channel[i].vu_r->set_max(24); |
| 894 | channel[i].vu_r->set_step(0.1); |
| 895 | |
| 896 | channel[i].peak_l = 0.0f; |
| 897 | channel[i].peak_r = 0.0f; |
| 898 | } |
| 899 | |
| 900 | EditorAudioMeterNotches *scale = memnew(EditorAudioMeterNotches); |
| 901 | |
| 902 | for (float db = 6.0f; db >= -80.0f; db -= 6.0f) { |
| 903 | bool renderNotch = (db >= -6.0f || db == -24.0f || db == -72.0f); |
| 904 | scale->add_notch(_scaled_db_to_normalized_volume(db), db, renderNotch); |
| 905 | } |
| 906 | scale->set_mouse_filter(MOUSE_FILTER_PASS); |
| 907 | hb->add_child(scale); |
| 908 | |
| 909 | effects = memnew(Tree); |
| 910 | effects->set_hide_root(true); |
| 911 | effects->set_custom_minimum_size(Size2(0, 80) * EDSCALE); |
| 912 | effects->set_hide_folding(true); |
| 913 | effects->set_v_size_flags(SIZE_EXPAND_FILL); |
| 914 | vb->add_child(effects); |
| 915 | effects->connect("item_edited" , callable_mp(this, &EditorAudioBus::_effect_edited)); |
| 916 | effects->connect("cell_selected" , callable_mp(this, &EditorAudioBus::_effect_selected)); |
| 917 | effects->set_edit_checkbox_cell_only_when_checkbox_is_pressed(true); |
| 918 | SET_DRAG_FORWARDING_GCD(effects, EditorAudioBus); |
| 919 | effects->connect("item_mouse_selected" , callable_mp(this, &EditorAudioBus::_effect_rmb)); |
| 920 | effects->set_allow_rmb_select(true); |
| 921 | effects->set_focus_mode(FOCUS_CLICK); |
| 922 | effects->set_allow_reselect(true); |
| 923 | effects->connect("gui_input" , callable_mp(this, &EditorAudioBus::_effects_gui_input)); |
| 924 | |
| 925 | send = memnew(OptionButton); |
| 926 | send->set_clip_text(true); |
| 927 | send->connect("item_selected" , callable_mp(this, &EditorAudioBus::_send_selected)); |
| 928 | vb->add_child(send); |
| 929 | |
| 930 | set_focus_mode(FOCUS_CLICK); |
| 931 | |
| 932 | effect_options = memnew(PopupMenu); |
| 933 | effect_options->connect("index_pressed" , callable_mp(this, &EditorAudioBus::_effect_add)); |
| 934 | add_child(effect_options); |
| 935 | List<StringName> effect_list; |
| 936 | ClassDB::get_inheriters_from_class("AudioEffect" , &effect_list); |
| 937 | effect_list.sort_custom<StringName::AlphCompare>(); |
| 938 | for (const StringName &E : effect_list) { |
| 939 | if (!ClassDB::can_instantiate(E) || ClassDB::is_virtual(E)) { |
| 940 | continue; |
| 941 | } |
| 942 | |
| 943 | String name = E.operator String().replace("AudioEffect" , "" ); |
| 944 | effect_options->add_item(name); |
| 945 | effect_options->set_item_metadata(-1, E); |
| 946 | } |
| 947 | |
| 948 | bus_options = memnew(MenuButton); |
| 949 | bus_options->set_shortcut_context(this); |
| 950 | bus_options->set_h_size_flags(SIZE_SHRINK_END); |
| 951 | bus_options->set_anchor(SIDE_RIGHT, 0.0); |
| 952 | bus_options->set_tooltip_text(TTR("Bus Options" )); |
| 953 | hbc->add_child(bus_options); |
| 954 | |
| 955 | bus_popup = bus_options->get_popup(); |
| 956 | bus_popup->add_shortcut(ED_SHORTCUT("audio_bus_editor/duplicate_selected_bus" , TTR("Duplicate Bus" ), KeyModifierMask::CMD_OR_CTRL | Key::D)); |
| 957 | bus_popup->add_shortcut(ED_SHORTCUT("audio_bus_editor/delete_selected_bus" , TTR("Delete Bus" ), Key::KEY_DELETE)); |
| 958 | bus_popup->set_item_disabled(1, is_master); |
| 959 | bus_popup->add_item(TTR("Reset Volume" )); |
| 960 | bus_popup->connect("index_pressed" , callable_mp(this, &EditorAudioBus::_bus_popup_pressed)); |
| 961 | |
| 962 | delete_effect_popup = memnew(PopupMenu); |
| 963 | delete_effect_popup->add_item(TTR("Delete Effect" )); |
| 964 | add_child(delete_effect_popup); |
| 965 | delete_effect_popup->connect("index_pressed" , callable_mp(this, &EditorAudioBus::_delete_effect_pressed)); |
| 966 | } |
| 967 | |
| 968 | void EditorAudioBusDrop::_notification(int p_what) { |
| 969 | switch (p_what) { |
| 970 | case NOTIFICATION_DRAW: { |
| 971 | draw_style_box(get_theme_stylebox(SNAME("normal" ), SNAME("Button" )), Rect2(Vector2(), get_size())); |
| 972 | |
| 973 | if (hovering_drop) { |
| 974 | Color accent = get_theme_color(SNAME("accent_color" ), EditorStringName(Editor)); |
| 975 | accent.a *= 0.7; |
| 976 | draw_rect(Rect2(Point2(), get_size()), accent, false); |
| 977 | } |
| 978 | } break; |
| 979 | |
| 980 | case NOTIFICATION_MOUSE_ENTER: { |
| 981 | if (!hovering_drop) { |
| 982 | hovering_drop = true; |
| 983 | queue_redraw(); |
| 984 | } |
| 985 | } break; |
| 986 | |
| 987 | case NOTIFICATION_MOUSE_EXIT: |
| 988 | case NOTIFICATION_DRAG_END: { |
| 989 | if (hovering_drop) { |
| 990 | hovering_drop = false; |
| 991 | queue_redraw(); |
| 992 | } |
| 993 | } break; |
| 994 | } |
| 995 | } |
| 996 | |
| 997 | bool EditorAudioBusDrop::can_drop_data(const Point2 &p_point, const Variant &p_data) const { |
| 998 | Dictionary d = p_data; |
| 999 | return (d.has("type" ) && String(d["type" ]) == "move_audio_bus" ); |
| 1000 | } |
| 1001 | |
| 1002 | void EditorAudioBusDrop::drop_data(const Point2 &p_point, const Variant &p_data) { |
| 1003 | Dictionary d = p_data; |
| 1004 | emit_signal(SNAME("dropped" ), d["index" ], AudioServer::get_singleton()->get_bus_count()); |
| 1005 | } |
| 1006 | |
| 1007 | void EditorAudioBusDrop::_bind_methods() { |
| 1008 | ADD_SIGNAL(MethodInfo("dropped" )); |
| 1009 | } |
| 1010 | |
| 1011 | EditorAudioBusDrop::EditorAudioBusDrop() { |
| 1012 | } |
| 1013 | |
| 1014 | void EditorAudioBuses::_set_renaming_buses(bool p_renaming) { |
| 1015 | renaming_buses = p_renaming; |
| 1016 | } |
| 1017 | |
| 1018 | void EditorAudioBuses::_update_buses() { |
| 1019 | if (renaming_buses) { |
| 1020 | // This case will be handled more gracefully, no need to trigger a full rebuild. |
| 1021 | // This is possibly a mistake in the AudioServer, which fires bus_layout_changed |
| 1022 | // on a rename. This may not be intended, but no way to tell at the moment. |
| 1023 | return; |
| 1024 | } |
| 1025 | |
| 1026 | for (int i = bus_hb->get_child_count() - 1; i >= 0; i--) { |
| 1027 | EditorAudioBus *audio_bus = Object::cast_to<EditorAudioBus>(bus_hb->get_child(i)); |
| 1028 | if (audio_bus) { |
| 1029 | bus_hb->remove_child(audio_bus); |
| 1030 | audio_bus->queue_free(); |
| 1031 | } |
| 1032 | } |
| 1033 | |
| 1034 | if (drop_end) { |
| 1035 | bus_hb->remove_child(drop_end); |
| 1036 | drop_end->queue_free(); |
| 1037 | drop_end = nullptr; |
| 1038 | } |
| 1039 | |
| 1040 | for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { |
| 1041 | bool is_master = (i == 0); |
| 1042 | EditorAudioBus *audio_bus = memnew(EditorAudioBus(this, is_master)); |
| 1043 | bus_hb->add_child(audio_bus); |
| 1044 | audio_bus->connect("delete_request" , callable_mp(this, &EditorAudioBuses::_delete_bus).bind(audio_bus), CONNECT_DEFERRED); |
| 1045 | audio_bus->connect("duplicate_request" , callable_mp(this, &EditorAudioBuses::_duplicate_bus), CONNECT_DEFERRED); |
| 1046 | audio_bus->connect("vol_reset_request" , callable_mp(this, &EditorAudioBuses::_reset_bus_volume).bind(audio_bus), CONNECT_DEFERRED); |
| 1047 | audio_bus->connect("drop_end_request" , callable_mp(this, &EditorAudioBuses::_request_drop_end)); |
| 1048 | audio_bus->connect("dropped" , callable_mp(this, &EditorAudioBuses::_drop_at_index), CONNECT_DEFERRED); |
| 1049 | } |
| 1050 | } |
| 1051 | |
| 1052 | EditorAudioBuses *EditorAudioBuses::register_editor() { |
| 1053 | EditorAudioBuses *audio_buses = memnew(EditorAudioBuses); |
| 1054 | EditorNode::get_singleton()->add_bottom_panel_item(TTR("Audio" ), audio_buses); |
| 1055 | return audio_buses; |
| 1056 | } |
| 1057 | |
| 1058 | void EditorAudioBuses::_notification(int p_what) { |
| 1059 | switch (p_what) { |
| 1060 | case NOTIFICATION_ENTER_TREE: |
| 1061 | case NOTIFICATION_THEME_CHANGED: { |
| 1062 | bus_scroll->add_theme_style_override("panel" , get_theme_stylebox(SNAME("panel" ), SNAME("Tree" ))); |
| 1063 | } break; |
| 1064 | |
| 1065 | case NOTIFICATION_READY: { |
| 1066 | _update_buses(); |
| 1067 | } break; |
| 1068 | |
| 1069 | case NOTIFICATION_DRAG_END: { |
| 1070 | if (drop_end) { |
| 1071 | bus_hb->remove_child(drop_end); |
| 1072 | drop_end->queue_free(); |
| 1073 | drop_end = nullptr; |
| 1074 | } |
| 1075 | } break; |
| 1076 | |
| 1077 | case NOTIFICATION_PROCESS: { |
| 1078 | // Check if anything was edited. |
| 1079 | bool edited = AudioServer::get_singleton()->is_edited(); |
| 1080 | for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { |
| 1081 | for (int j = 0; j < AudioServer::get_singleton()->get_bus_effect_count(i); j++) { |
| 1082 | Ref<AudioEffect> effect = AudioServer::get_singleton()->get_bus_effect(i, j); |
| 1083 | if (effect->is_edited()) { |
| 1084 | edited = true; |
| 1085 | effect->set_edited(false); |
| 1086 | } |
| 1087 | } |
| 1088 | } |
| 1089 | |
| 1090 | AudioServer::get_singleton()->set_edited(false); |
| 1091 | |
| 1092 | if (edited) { |
| 1093 | save_timer->start(); |
| 1094 | } |
| 1095 | } break; |
| 1096 | } |
| 1097 | } |
| 1098 | |
| 1099 | void EditorAudioBuses::_add_bus() { |
| 1100 | EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); |
| 1101 | |
| 1102 | ur->create_action(TTR("Add Audio Bus" )); |
| 1103 | ur->add_do_method(AudioServer::get_singleton(), "set_bus_count" , AudioServer::get_singleton()->get_bus_count() + 1); |
| 1104 | ur->add_undo_method(AudioServer::get_singleton(), "set_bus_count" , AudioServer::get_singleton()->get_bus_count()); |
| 1105 | ur->add_do_method(this, "_update_buses" ); |
| 1106 | ur->add_undo_method(this, "_update_buses" ); |
| 1107 | ur->commit_action(); |
| 1108 | } |
| 1109 | |
| 1110 | void EditorAudioBuses::_update_bus(int p_index) { |
| 1111 | if (p_index >= bus_hb->get_child_count()) { |
| 1112 | return; |
| 1113 | } |
| 1114 | |
| 1115 | bus_hb->get_child(p_index)->call("update_bus" ); |
| 1116 | } |
| 1117 | |
| 1118 | void EditorAudioBuses::_update_sends() { |
| 1119 | for (int i = 0; i < bus_hb->get_child_count(); i++) { |
| 1120 | bus_hb->get_child(i)->call("update_send" ); |
| 1121 | } |
| 1122 | } |
| 1123 | |
| 1124 | void EditorAudioBuses::_delete_bus(Object *p_which) { |
| 1125 | EditorAudioBus *bus = Object::cast_to<EditorAudioBus>(p_which); |
| 1126 | int index = bus->get_index(); |
| 1127 | if (index == 0) { |
| 1128 | EditorNode::get_singleton()->show_warning(TTR("Master bus can't be deleted!" )); |
| 1129 | return; |
| 1130 | } |
| 1131 | |
| 1132 | EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); |
| 1133 | |
| 1134 | ur->create_action(TTR("Delete Audio Bus" )); |
| 1135 | ur->add_do_method(AudioServer::get_singleton(), "remove_bus" , index); |
| 1136 | ur->add_undo_method(AudioServer::get_singleton(), "add_bus" , index); |
| 1137 | ur->add_undo_method(AudioServer::get_singleton(), "set_bus_name" , index, AudioServer::get_singleton()->get_bus_name(index)); |
| 1138 | ur->add_undo_method(AudioServer::get_singleton(), "set_bus_volume_db" , index, AudioServer::get_singleton()->get_bus_volume_db(index)); |
| 1139 | ur->add_undo_method(AudioServer::get_singleton(), "set_bus_send" , index, AudioServer::get_singleton()->get_bus_send(index)); |
| 1140 | ur->add_undo_method(AudioServer::get_singleton(), "set_bus_solo" , index, AudioServer::get_singleton()->is_bus_solo(index)); |
| 1141 | ur->add_undo_method(AudioServer::get_singleton(), "set_bus_mute" , index, AudioServer::get_singleton()->is_bus_mute(index)); |
| 1142 | ur->add_undo_method(AudioServer::get_singleton(), "set_bus_bypass_effects" , index, AudioServer::get_singleton()->is_bus_bypassing_effects(index)); |
| 1143 | for (int i = 0; i < AudioServer::get_singleton()->get_bus_effect_count(index); i++) { |
| 1144 | ur->add_undo_method(AudioServer::get_singleton(), "add_bus_effect" , index, AudioServer::get_singleton()->get_bus_effect(index, i)); |
| 1145 | ur->add_undo_method(AudioServer::get_singleton(), "set_bus_effect_enabled" , index, i, AudioServer::get_singleton()->is_bus_effect_enabled(index, i)); |
| 1146 | } |
| 1147 | ur->add_do_method(this, "_update_buses" ); |
| 1148 | ur->add_undo_method(this, "_update_buses" ); |
| 1149 | ur->commit_action(); |
| 1150 | } |
| 1151 | |
| 1152 | void EditorAudioBuses::_duplicate_bus(int p_which) { |
| 1153 | int add_at_pos = p_which + 1; |
| 1154 | EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); |
| 1155 | ur->create_action(TTR("Duplicate Audio Bus" )); |
| 1156 | ur->add_do_method(AudioServer::get_singleton(), "add_bus" , add_at_pos); |
| 1157 | ur->add_do_method(AudioServer::get_singleton(), "set_bus_name" , add_at_pos, AudioServer::get_singleton()->get_bus_name(p_which) + " Copy" ); |
| 1158 | ur->add_do_method(AudioServer::get_singleton(), "set_bus_volume_db" , add_at_pos, AudioServer::get_singleton()->get_bus_volume_db(p_which)); |
| 1159 | ur->add_do_method(AudioServer::get_singleton(), "set_bus_send" , add_at_pos, AudioServer::get_singleton()->get_bus_send(p_which)); |
| 1160 | ur->add_do_method(AudioServer::get_singleton(), "set_bus_solo" , add_at_pos, AudioServer::get_singleton()->is_bus_solo(p_which)); |
| 1161 | ur->add_do_method(AudioServer::get_singleton(), "set_bus_mute" , add_at_pos, AudioServer::get_singleton()->is_bus_mute(p_which)); |
| 1162 | ur->add_do_method(AudioServer::get_singleton(), "set_bus_bypass_effects" , add_at_pos, AudioServer::get_singleton()->is_bus_bypassing_effects(p_which)); |
| 1163 | for (int i = 0; i < AudioServer::get_singleton()->get_bus_effect_count(p_which); i++) { |
| 1164 | ur->add_do_method(AudioServer::get_singleton(), "add_bus_effect" , add_at_pos, AudioServer::get_singleton()->get_bus_effect(p_which, i)); |
| 1165 | ur->add_do_method(AudioServer::get_singleton(), "set_bus_effect_enabled" , add_at_pos, i, AudioServer::get_singleton()->is_bus_effect_enabled(p_which, i)); |
| 1166 | } |
| 1167 | ur->add_undo_method(AudioServer::get_singleton(), "remove_bus" , add_at_pos); |
| 1168 | ur->add_do_method(this, "_update_buses" ); |
| 1169 | ur->add_undo_method(this, "_update_buses" ); |
| 1170 | ur->commit_action(); |
| 1171 | } |
| 1172 | |
| 1173 | void EditorAudioBuses::_reset_bus_volume(Object *p_which) { |
| 1174 | EditorAudioBus *bus = Object::cast_to<EditorAudioBus>(p_which); |
| 1175 | int index = bus->get_index(); |
| 1176 | |
| 1177 | EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); |
| 1178 | ur->create_action(TTR("Reset Bus Volume" )); |
| 1179 | ur->add_do_method(AudioServer::get_singleton(), "set_bus_volume_db" , index, 0.f); |
| 1180 | ur->add_undo_method(AudioServer::get_singleton(), "set_bus_volume_db" , index, AudioServer::get_singleton()->get_bus_volume_db(index)); |
| 1181 | ur->add_do_method(this, "_update_buses" ); |
| 1182 | ur->add_undo_method(this, "_update_buses" ); |
| 1183 | ur->commit_action(); |
| 1184 | } |
| 1185 | |
| 1186 | void EditorAudioBuses::_request_drop_end() { |
| 1187 | if (!drop_end && bus_hb->get_child_count()) { |
| 1188 | drop_end = memnew(EditorAudioBusDrop); |
| 1189 | |
| 1190 | bus_hb->add_child(drop_end); |
| 1191 | drop_end->set_custom_minimum_size(Object::cast_to<Control>(bus_hb->get_child(0))->get_size()); |
| 1192 | drop_end->connect("dropped" , callable_mp(this, &EditorAudioBuses::_drop_at_index), CONNECT_DEFERRED); |
| 1193 | } |
| 1194 | } |
| 1195 | |
| 1196 | void EditorAudioBuses::_drop_at_index(int p_bus, int p_index) { |
| 1197 | EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); |
| 1198 | ur->create_action(TTR("Move Audio Bus" )); |
| 1199 | |
| 1200 | ur->add_do_method(AudioServer::get_singleton(), "move_bus" , p_bus, p_index); |
| 1201 | int real_bus = p_index > p_bus ? p_bus : p_bus + 1; |
| 1202 | int real_index = p_index > p_bus ? p_index - 1 : p_index; |
| 1203 | ur->add_undo_method(AudioServer::get_singleton(), "move_bus" , real_index, real_bus); |
| 1204 | |
| 1205 | ur->add_do_method(this, "_update_buses" ); |
| 1206 | ur->add_undo_method(this, "_update_buses" ); |
| 1207 | ur->commit_action(); |
| 1208 | } |
| 1209 | |
| 1210 | void EditorAudioBuses::_server_save() { |
| 1211 | Ref<AudioBusLayout> state = AudioServer::get_singleton()->generate_bus_layout(); |
| 1212 | ResourceSaver::save(state, edited_path); |
| 1213 | } |
| 1214 | |
| 1215 | void EditorAudioBuses::_select_layout() { |
| 1216 | FileSystemDock::get_singleton()->select_file(edited_path); |
| 1217 | } |
| 1218 | |
| 1219 | void EditorAudioBuses::_save_as_layout() { |
| 1220 | file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE); |
| 1221 | file_dialog->set_title(TTR("Save Audio Bus Layout As..." )); |
| 1222 | file_dialog->set_current_path(edited_path); |
| 1223 | file_dialog->popup_file_dialog(); |
| 1224 | new_layout = false; |
| 1225 | } |
| 1226 | |
| 1227 | void EditorAudioBuses::_new_layout() { |
| 1228 | file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE); |
| 1229 | file_dialog->set_title(TTR("Location for New Layout..." )); |
| 1230 | file_dialog->set_current_path(edited_path); |
| 1231 | file_dialog->popup_file_dialog(); |
| 1232 | new_layout = true; |
| 1233 | } |
| 1234 | |
| 1235 | void EditorAudioBuses::_load_layout() { |
| 1236 | file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE); |
| 1237 | file_dialog->set_title(TTR("Open Audio Bus Layout" )); |
| 1238 | file_dialog->set_current_path(edited_path); |
| 1239 | file_dialog->popup_file_dialog(); |
| 1240 | new_layout = false; |
| 1241 | } |
| 1242 | |
| 1243 | void EditorAudioBuses::_load_default_layout() { |
| 1244 | String layout_path = GLOBAL_GET("audio/buses/default_bus_layout" ); |
| 1245 | |
| 1246 | Ref<AudioBusLayout> state = ResourceLoader::load(layout_path, "" , ResourceFormatLoader::CACHE_MODE_IGNORE); |
| 1247 | if (state.is_null()) { |
| 1248 | EditorNode::get_singleton()->show_warning(vformat(TTR("There is no '%s' file." ), layout_path)); |
| 1249 | return; |
| 1250 | } |
| 1251 | |
| 1252 | edited_path = layout_path; |
| 1253 | file->set_text(String(TTR("Layout:" )) + " " + layout_path.get_file()); |
| 1254 | AudioServer::get_singleton()->set_bus_layout(state); |
| 1255 | _update_buses(); |
| 1256 | EditorUndoRedoManager::get_singleton()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY); |
| 1257 | call_deferred(SNAME("_select_layout" )); |
| 1258 | } |
| 1259 | |
| 1260 | void EditorAudioBuses::_file_dialog_callback(const String &p_string) { |
| 1261 | if (file_dialog->get_file_mode() == EditorFileDialog::FILE_MODE_OPEN_FILE) { |
| 1262 | Ref<AudioBusLayout> state = ResourceLoader::load(p_string, "" , ResourceFormatLoader::CACHE_MODE_IGNORE); |
| 1263 | if (state.is_null()) { |
| 1264 | EditorNode::get_singleton()->show_warning(TTR("Invalid file, not an audio bus layout." )); |
| 1265 | return; |
| 1266 | } |
| 1267 | |
| 1268 | edited_path = p_string; |
| 1269 | file->set_text(String(TTR("Layout:" )) + " " + p_string.get_file()); |
| 1270 | AudioServer::get_singleton()->set_bus_layout(state); |
| 1271 | _update_buses(); |
| 1272 | EditorUndoRedoManager::get_singleton()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY); |
| 1273 | call_deferred(SNAME("_select_layout" )); |
| 1274 | |
| 1275 | } else if (file_dialog->get_file_mode() == EditorFileDialog::FILE_MODE_SAVE_FILE) { |
| 1276 | if (new_layout) { |
| 1277 | Ref<AudioBusLayout> empty_state; |
| 1278 | empty_state.instantiate(); |
| 1279 | AudioServer::get_singleton()->set_bus_layout(empty_state); |
| 1280 | } |
| 1281 | |
| 1282 | Error err = ResourceSaver::save(AudioServer::get_singleton()->generate_bus_layout(), p_string); |
| 1283 | |
| 1284 | if (err != OK) { |
| 1285 | EditorNode::get_singleton()->show_warning(vformat(TTR("Error saving file: %s" ), p_string)); |
| 1286 | return; |
| 1287 | } |
| 1288 | |
| 1289 | edited_path = p_string; |
| 1290 | file->set_text(String(TTR("Layout:" )) + " " + p_string.get_file()); |
| 1291 | _update_buses(); |
| 1292 | EditorUndoRedoManager::get_singleton()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY); |
| 1293 | call_deferred(SNAME("_select_layout" )); |
| 1294 | } |
| 1295 | } |
| 1296 | |
| 1297 | void EditorAudioBuses::_bind_methods() { |
| 1298 | ClassDB::bind_method("_set_renaming_buses" , &EditorAudioBuses::_set_renaming_buses); |
| 1299 | ClassDB::bind_method("_update_buses" , &EditorAudioBuses::_update_buses); |
| 1300 | ClassDB::bind_method("_update_bus" , &EditorAudioBuses::_update_bus); |
| 1301 | ClassDB::bind_method("_update_sends" , &EditorAudioBuses::_update_sends); |
| 1302 | ClassDB::bind_method("_select_layout" , &EditorAudioBuses::_select_layout); |
| 1303 | } |
| 1304 | |
| 1305 | EditorAudioBuses::EditorAudioBuses() { |
| 1306 | top_hb = memnew(HBoxContainer); |
| 1307 | add_child(top_hb); |
| 1308 | |
| 1309 | file = memnew(Label); |
| 1310 | String layout_path = GLOBAL_GET("audio/buses/default_bus_layout" ); |
| 1311 | file->set_text(String(TTR("Layout:" )) + " " + layout_path.get_file()); |
| 1312 | file->set_clip_text(true); |
| 1313 | file->set_h_size_flags(SIZE_EXPAND_FILL); |
| 1314 | top_hb->add_child(file); |
| 1315 | |
| 1316 | add = memnew(Button); |
| 1317 | top_hb->add_child(add); |
| 1318 | add->set_text(TTR("Add Bus" )); |
| 1319 | add->set_tooltip_text(TTR("Add a new Audio Bus to this layout." )); |
| 1320 | add->connect("pressed" , callable_mp(this, &EditorAudioBuses::_add_bus)); |
| 1321 | |
| 1322 | VSeparator *separator = memnew(VSeparator); |
| 1323 | top_hb->add_child(separator); |
| 1324 | |
| 1325 | load = memnew(Button); |
| 1326 | load->set_text(TTR("Load" )); |
| 1327 | load->set_tooltip_text(TTR("Load an existing Bus Layout." )); |
| 1328 | top_hb->add_child(load); |
| 1329 | load->connect("pressed" , callable_mp(this, &EditorAudioBuses::_load_layout)); |
| 1330 | |
| 1331 | save_as = memnew(Button); |
| 1332 | save_as->set_text(TTR("Save As" )); |
| 1333 | save_as->set_tooltip_text(TTR("Save this Bus Layout to a file." )); |
| 1334 | top_hb->add_child(save_as); |
| 1335 | save_as->connect("pressed" , callable_mp(this, &EditorAudioBuses::_save_as_layout)); |
| 1336 | |
| 1337 | _default = memnew(Button); |
| 1338 | _default->set_text(TTR("Load Default" )); |
| 1339 | _default->set_tooltip_text(TTR("Load the default Bus Layout." )); |
| 1340 | top_hb->add_child(_default); |
| 1341 | _default->connect("pressed" , callable_mp(this, &EditorAudioBuses::_load_default_layout)); |
| 1342 | |
| 1343 | _new = memnew(Button); |
| 1344 | _new->set_text(TTR("Create" )); |
| 1345 | _new->set_tooltip_text(TTR("Create a new Bus Layout." )); |
| 1346 | top_hb->add_child(_new); |
| 1347 | _new->connect("pressed" , callable_mp(this, &EditorAudioBuses::_new_layout)); |
| 1348 | |
| 1349 | bus_scroll = memnew(ScrollContainer); |
| 1350 | bus_scroll->set_v_size_flags(SIZE_EXPAND_FILL); |
| 1351 | bus_scroll->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED); |
| 1352 | add_child(bus_scroll); |
| 1353 | bus_hb = memnew(HBoxContainer); |
| 1354 | bus_hb->set_v_size_flags(SIZE_EXPAND_FILL); |
| 1355 | bus_scroll->add_child(bus_hb); |
| 1356 | |
| 1357 | save_timer = memnew(Timer); |
| 1358 | save_timer->set_wait_time(0.8); |
| 1359 | save_timer->set_one_shot(true); |
| 1360 | add_child(save_timer); |
| 1361 | save_timer->connect("timeout" , callable_mp(this, &EditorAudioBuses::_server_save)); |
| 1362 | |
| 1363 | set_v_size_flags(SIZE_EXPAND_FILL); |
| 1364 | |
| 1365 | edited_path = GLOBAL_GET("audio/buses/default_bus_layout" ); |
| 1366 | |
| 1367 | file_dialog = memnew(EditorFileDialog); |
| 1368 | List<String> ext; |
| 1369 | ResourceLoader::get_recognized_extensions_for_type("AudioBusLayout" , &ext); |
| 1370 | for (const String &E : ext) { |
| 1371 | file_dialog->add_filter("*." + E, TTR("Audio Bus Layout" )); |
| 1372 | } |
| 1373 | add_child(file_dialog); |
| 1374 | file_dialog->connect("file_selected" , callable_mp(this, &EditorAudioBuses::_file_dialog_callback)); |
| 1375 | |
| 1376 | AudioServer::get_singleton()->connect("bus_layout_changed" , callable_mp(this, &EditorAudioBuses::_update_buses)); |
| 1377 | |
| 1378 | set_process(true); |
| 1379 | } |
| 1380 | |
| 1381 | void EditorAudioBuses::open_layout(const String &p_path) { |
| 1382 | EditorNode::get_singleton()->make_bottom_panel_item_visible(this); |
| 1383 | |
| 1384 | Ref<AudioBusLayout> state = ResourceLoader::load(p_path, "" , ResourceFormatLoader::CACHE_MODE_IGNORE); |
| 1385 | if (state.is_null()) { |
| 1386 | EditorNode::get_singleton()->show_warning(TTR("Invalid file, not an audio bus layout." )); |
| 1387 | return; |
| 1388 | } |
| 1389 | |
| 1390 | edited_path = p_path; |
| 1391 | file->set_text(p_path.get_file()); |
| 1392 | AudioServer::get_singleton()->set_bus_layout(state); |
| 1393 | _update_buses(); |
| 1394 | EditorUndoRedoManager::get_singleton()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY); |
| 1395 | call_deferred(SNAME("_select_layout" )); |
| 1396 | } |
| 1397 | |
| 1398 | void AudioBusesEditorPlugin::edit(Object *p_node) { |
| 1399 | if (Object::cast_to<AudioBusLayout>(p_node)) { |
| 1400 | String path = Object::cast_to<AudioBusLayout>(p_node)->get_path(); |
| 1401 | if (path.is_resource_file()) { |
| 1402 | audio_bus_editor->open_layout(path); |
| 1403 | } |
| 1404 | } |
| 1405 | } |
| 1406 | |
| 1407 | bool AudioBusesEditorPlugin::handles(Object *p_node) const { |
| 1408 | return (Object::cast_to<AudioBusLayout>(p_node) != nullptr); |
| 1409 | } |
| 1410 | |
| 1411 | void AudioBusesEditorPlugin::make_visible(bool p_visible) { |
| 1412 | } |
| 1413 | |
| 1414 | AudioBusesEditorPlugin::AudioBusesEditorPlugin(EditorAudioBuses *p_node) { |
| 1415 | audio_bus_editor = p_node; |
| 1416 | } |
| 1417 | |
| 1418 | AudioBusesEditorPlugin::~AudioBusesEditorPlugin() { |
| 1419 | } |
| 1420 | |
| 1421 | void EditorAudioMeterNotches::add_notch(float p_normalized_offset, float p_db_value, bool p_render_value) { |
| 1422 | notches.push_back(AudioNotch(p_normalized_offset, p_db_value, p_render_value)); |
| 1423 | } |
| 1424 | |
| 1425 | Size2 EditorAudioMeterNotches::get_minimum_size() const { |
| 1426 | Ref<Font> font = get_theme_font(SNAME("font" ), SNAME("Label" )); |
| 1427 | int font_size = get_theme_font_size(SNAME("font_size" ), SNAME("Label" )); |
| 1428 | float font_height = font->get_height(font_size); |
| 1429 | |
| 1430 | float width = 0; |
| 1431 | float height = top_padding + btm_padding; |
| 1432 | |
| 1433 | for (int i = 0; i < notches.size(); i++) { |
| 1434 | if (notches[i].render_db_value) { |
| 1435 | width = MAX(width, font->get_string_size(String::num(Math::abs(notches[i].db_value)) + "dB" , HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x); |
| 1436 | height += font_height; |
| 1437 | } |
| 1438 | } |
| 1439 | width += line_length + label_space; |
| 1440 | |
| 1441 | return Size2(width, height); |
| 1442 | } |
| 1443 | |
| 1444 | void EditorAudioMeterNotches::_update_theme_item_cache() { |
| 1445 | Control::_update_theme_item_cache(); |
| 1446 | |
| 1447 | theme_cache.notch_color = get_theme_color(SNAME("font_color" ), EditorStringName(Editor)); |
| 1448 | |
| 1449 | theme_cache.font = get_theme_font(SNAME("font" ), SNAME("Label" )); |
| 1450 | theme_cache.font_size = get_theme_font_size(SNAME("font_size" ), SNAME("Label" )); |
| 1451 | } |
| 1452 | |
| 1453 | void EditorAudioMeterNotches::_bind_methods() { |
| 1454 | ClassDB::bind_method("add_notch" , &EditorAudioMeterNotches::add_notch); |
| 1455 | ClassDB::bind_method("_draw_audio_notches" , &EditorAudioMeterNotches::_draw_audio_notches); |
| 1456 | } |
| 1457 | |
| 1458 | void EditorAudioMeterNotches::_notification(int p_what) { |
| 1459 | switch (p_what) { |
| 1460 | case NOTIFICATION_DRAW: { |
| 1461 | _draw_audio_notches(); |
| 1462 | } break; |
| 1463 | } |
| 1464 | } |
| 1465 | |
| 1466 | void EditorAudioMeterNotches::_draw_audio_notches() { |
| 1467 | float font_height = theme_cache.font->get_height(theme_cache.font_size); |
| 1468 | |
| 1469 | for (int i = 0; i < notches.size(); i++) { |
| 1470 | AudioNotch n = notches[i]; |
| 1471 | draw_line(Vector2(0, (1.0f - n.relative_position) * (get_size().y - btm_padding - top_padding) + top_padding), |
| 1472 | Vector2(line_length * EDSCALE, (1.0f - n.relative_position) * (get_size().y - btm_padding - top_padding) + top_padding), |
| 1473 | theme_cache.notch_color, |
| 1474 | Math::round(EDSCALE)); |
| 1475 | |
| 1476 | if (n.render_db_value) { |
| 1477 | draw_string(theme_cache.font, |
| 1478 | Vector2((line_length + label_space) * EDSCALE, |
| 1479 | (1.0f - n.relative_position) * (get_size().y - btm_padding - top_padding) + (font_height / 4) + top_padding), |
| 1480 | String::num(Math::abs(n.db_value)) + "dB" , |
| 1481 | HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size, |
| 1482 | theme_cache.notch_color); |
| 1483 | } |
| 1484 | } |
| 1485 | } |
| 1486 | |