diff options
| -rw-r--r-- | editor/plugins/audio_stream_editor_plugin.cpp | 279 | ||||
| -rw-r--r-- | editor/plugins/audio_stream_editor_plugin.h | 93 | ||||
| -rw-r--r-- | editor/register_editor_types.cpp | 2 | 
3 files changed, 374 insertions, 0 deletions
diff --git a/editor/plugins/audio_stream_editor_plugin.cpp b/editor/plugins/audio_stream_editor_plugin.cpp new file mode 100644 index 0000000000..719ae09f47 --- /dev/null +++ b/editor/plugins/audio_stream_editor_plugin.cpp @@ -0,0 +1,279 @@ +/*************************************************************************/ +/*  audio_stream_editor_plugin.cpp                                       */ +/*************************************************************************/ +/*                       This file is part of:                           */ +/*                           GODOT ENGINE                                */ +/*                      https://godotengine.org                          */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */ +/*                                                                       */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the       */ +/* "Software"), to deal in the Software without restriction, including   */ +/* without limitation the rights to use, copy, modify, merge, publish,   */ +/* distribute, sublicense, and/or sell copies of the Software, and to    */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions:                                             */ +/*                                                                       */ +/* The above copyright notice and this permission notice shall be        */ +/* included in all copies or substantial portions of the Software.       */ +/*                                                                       */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */ +/*************************************************************************/ + +#include "audio_stream_editor_plugin.h" + +#include "core/core_string_names.h" +#include "editor/audio_stream_preview.h" +#include "editor/editor_scale.h" +#include "editor/editor_settings.h" + +// AudioStreamEditor + +void AudioStreamEditor::_notification(int p_what) { +	switch (p_what) { +		case NOTIFICATION_READY: { +			AudioStreamPreviewGenerator::get_singleton()->connect(SNAME("preview_updated"), callable_mp(this, &AudioStreamEditor::_preview_changed)); +		} break; +		case NOTIFICATION_THEME_CHANGED: +		case NOTIFICATION_ENTER_TREE: { +			Ref<Font> font = get_theme_font(SNAME("status_source"), SNAME("EditorFonts")); + +			_current_label->add_theme_font_override(SNAME("font"), font); +			_duration_label->add_theme_font_override(SNAME("font"), font); + +			_play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons"))); +			_stop_button->set_icon(get_theme_icon(SNAME("Stop"), SNAME("EditorIcons"))); +			_preview->set_color(get_theme_color(SNAME("dark_color_2"), SNAME("Editor"))); + +			set_color(get_theme_color(SNAME("dark_color_1"), SNAME("Editor"))); + +			_indicator->queue_redraw(); +			_preview->queue_redraw(); +		} break; +		case NOTIFICATION_PROCESS: { +			_current = _player->get_playback_position(); +			_indicator->queue_redraw(); +		} break; +		case NOTIFICATION_VISIBILITY_CHANGED: { +			if (!is_visible_in_tree()) { +				_stop(); +			} +		} break; +		default: { +		} break; +	} +} + +void AudioStreamEditor::_draw_preview() { +	Rect2 rect = _preview->get_rect(); +	Size2 size = get_size(); + +	Ref<AudioStreamPreview> preview = AudioStreamPreviewGenerator::get_singleton()->generate_preview(stream); +	float preview_len = preview->get_length(); + +	Vector<Vector2> lines; +	lines.resize(size.width * 2); + +	for (int i = 0; i < size.width; i++) { +		float ofs = i * preview_len / size.width; +		float ofs_n = (i + 1) * preview_len / size.width; +		float max = preview->get_max(ofs, ofs_n) * 0.5 + 0.5; +		float min = preview->get_min(ofs, ofs_n) * 0.5 + 0.5; + +		int idx = i; +		lines.write[idx * 2 + 0] = Vector2(i + 1, rect.position.y + min * rect.size.y); +		lines.write[idx * 2 + 1] = Vector2(i + 1, rect.position.y + max * rect.size.y); +	} + +	RS::get_singleton()->canvas_item_add_multiline(_preview->get_canvas_item(), lines, { get_theme_color(SNAME("contrast_color_2"), SNAME("Editor")) }); +} + +void AudioStreamEditor::_preview_changed(ObjectID p_which) { +	if (stream.is_valid() && stream->get_instance_id() == p_which) { +		_preview->queue_redraw(); +	} +} + +void AudioStreamEditor::_stream_changed() { +	if (!is_visible()) { +		return; +	} +	queue_redraw(); +} + +void AudioStreamEditor::_play() { +	if (_player->is_playing()) { +		_pausing = true; +		_player->stop(); +		_play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons"))); +		set_process(false); +	} else { +		_pausing = false; +		_player->play(_current); +		_play_button->set_icon(get_theme_icon(SNAME("Pause"), SNAME("EditorIcons"))); +		set_process(true); +	} +} + +void AudioStreamEditor::_stop() { +	_player->stop(); +	_play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons"))); +	_current = 0; +	_indicator->queue_redraw(); +	set_process(false); +} + +void AudioStreamEditor::_on_finished() { +	_play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons"))); +	if (!_pausing) { +		_current = 0; +		_indicator->queue_redraw(); +	} else { +		_pausing = false; +	} +	set_process(false); +} + +void AudioStreamEditor::_draw_indicator() { +	if (stream.is_null()) { +		return; +	} + +	Rect2 rect = _preview->get_rect(); +	float len = stream->get_length(); +	float ofs_x = _current / len * rect.size.width; +	const Color col = get_theme_color(SNAME("accent_color"), SNAME("Editor")); +	Ref<Texture2D> icon = get_theme_icon(SNAME("TimelineIndicator"), SNAME("EditorIcons")); +	_indicator->draw_line(Point2(ofs_x, 0), Point2(ofs_x, rect.size.height), col, Math::round(2 * EDSCALE)); +	_indicator->draw_texture( +			icon, +			Point2(ofs_x - icon->get_width() * 0.5, 0), +			col); + +	_current_label->set_text(String::num(_current, 2).pad_decimals(2) + " /"); +} + +void AudioStreamEditor::_on_input_indicator(Ref<InputEvent> p_event) { +	const Ref<InputEventMouseButton> mb = p_event; +	if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) { +		if (mb->is_pressed()) { +			_seek_to(mb->get_position().x); +		} +		_dragging = mb->is_pressed(); +	} + +	const Ref<InputEventMouseMotion> mm = p_event; +	if (mm.is_valid()) { +		if (_dragging) { +			_seek_to(mm->get_position().x); +		} +	} +} + +void AudioStreamEditor::_seek_to(real_t p_x) { +	_current = p_x / _preview->get_rect().size.x * stream->get_length(); +	_current = CLAMP(_current, 0, stream->get_length()); +	_player->seek(_current); +	_indicator->queue_redraw(); +} + +void AudioStreamEditor::set_stream(const Ref<AudioStream> &p_stream) { +	if (stream.is_valid()) { +		stream->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &AudioStreamEditor::_stream_changed)); +	} + +	stream = p_stream; +	if (stream.is_null()) { +		hide(); +		return; +	} +	stream->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &AudioStreamEditor::_stream_changed)); + +	_player->set_stream(stream); +	_current = 0; + +	String text = String::num(stream->get_length(), 2).pad_decimals(2) + "s"; +	_duration_label->set_text(text); + +	queue_redraw(); +} + +AudioStreamEditor::AudioStreamEditor() { +	set_custom_minimum_size(Size2(1, 100) * EDSCALE); + +	_player = memnew(AudioStreamPlayer); +	_player->connect(SNAME("finished"), callable_mp(this, &AudioStreamEditor::_on_finished)); +	add_child(_player); + +	VBoxContainer *vbox = memnew(VBoxContainer); +	vbox->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); +	add_child(vbox); + +	_preview = memnew(ColorRect); +	_preview->set_v_size_flags(SIZE_EXPAND_FILL); +	_preview->connect(SNAME("draw"), callable_mp(this, &AudioStreamEditor::_draw_preview)); +	vbox->add_child(_preview); + +	_indicator = memnew(Control); +	_indicator->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); +	_indicator->connect(SNAME("draw"), callable_mp(this, &AudioStreamEditor::_draw_indicator)); +	_indicator->connect(SNAME("gui_input"), callable_mp(this, &AudioStreamEditor::_on_input_indicator)); +	_preview->add_child(_indicator); + +	HBoxContainer *hbox = memnew(HBoxContainer); +	hbox->add_theme_constant_override("separation", 0); +	vbox->add_child(hbox); + +	_play_button = memnew(Button); +	hbox->add_child(_play_button); +	_play_button->set_flat(true); +	_play_button->set_focus_mode(Control::FOCUS_NONE); +	_play_button->connect(SNAME("pressed"), callable_mp(this, &AudioStreamEditor::_play)); +	_play_button->set_shortcut(ED_SHORTCUT("audio_stream_editor/audio_preview_play_pause", TTR("Audio Preview Play/Pause"), Key::SPACE)); + +	_stop_button = memnew(Button); +	hbox->add_child(_stop_button); +	_stop_button->set_flat(true); +	_stop_button->set_focus_mode(Control::FOCUS_NONE); +	_stop_button->connect(SNAME("pressed"), callable_mp(this, &AudioStreamEditor::_stop)); + +	_current_label = memnew(Label); +	_current_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); +	_current_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); +	_current_label->set_modulate(Color(1, 1, 1, 0.5)); +	hbox->add_child(_current_label); + +	_duration_label = memnew(Label); +	hbox->add_child(_duration_label); +} + +// EditorInspectorPluginAudioStream + +bool EditorInspectorPluginAudioStream::can_handle(Object *p_object) { +	return Object::cast_to<AudioStream>(p_object) != nullptr; +} + +void EditorInspectorPluginAudioStream::parse_begin(Object *p_object) { +	AudioStream *stream = Object::cast_to<AudioStream>(p_object); + +	editor = memnew(AudioStreamEditor); +	editor->set_stream(Ref<AudioStream>(stream)); + +	add_custom_control(editor); +} + +// AudioStreamEditorPlugin + +AudioStreamEditorPlugin::AudioStreamEditorPlugin() { +	Ref<EditorInspectorPluginAudioStream> plugin; +	plugin.instantiate(); +	add_inspector_plugin(plugin); +} diff --git a/editor/plugins/audio_stream_editor_plugin.h b/editor/plugins/audio_stream_editor_plugin.h new file mode 100644 index 0000000000..13e52929ee --- /dev/null +++ b/editor/plugins/audio_stream_editor_plugin.h @@ -0,0 +1,93 @@ +/*************************************************************************/ +/*  audio_stream_editor_plugin.h                                         */ +/*************************************************************************/ +/*                       This file is part of:                           */ +/*                           GODOT ENGINE                                */ +/*                      https://godotengine.org                          */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */ +/*                                                                       */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the       */ +/* "Software"), to deal in the Software without restriction, including   */ +/* without limitation the rights to use, copy, modify, merge, publish,   */ +/* distribute, sublicense, and/or sell copies of the Software, and to    */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions:                                             */ +/*                                                                       */ +/* The above copyright notice and this permission notice shall be        */ +/* included in all copies or substantial portions of the Software.       */ +/*                                                                       */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */ +/*************************************************************************/ + +#ifndef AUDIO_STREAM_EDITOR_PLUGIN_H +#define AUDIO_STREAM_EDITOR_PLUGIN_H + +#include "editor/editor_inspector.h" +#include "editor/editor_plugin.h" +#include "scene/audio/audio_stream_player.h" +#include "scene/gui/button.h" +#include "scene/gui/color_rect.h" +#include "scene/gui/label.h" + +class AudioStreamEditor : public ColorRect { +	GDCLASS(AudioStreamEditor, ColorRect); + +	Ref<AudioStream> stream; + +	AudioStreamPlayer *_player = nullptr; +	ColorRect *_preview = nullptr; +	Control *_indicator = nullptr; +	Label *_current_label = nullptr; +	Label *_duration_label = nullptr; + +	Button *_play_button = nullptr; +	Button *_stop_button = nullptr; + +	float _current = 0; +	bool _dragging = false; +	bool _pausing = false; + +protected: +	void _notification(int p_what); +	void _preview_changed(ObjectID p_which); +	void _play(); +	void _stop(); +	void _on_finished(); +	void _draw_preview(); +	void _draw_indicator(); +	void _on_input_indicator(Ref<InputEvent> p_event); +	void _seek_to(real_t p_x); +	void _stream_changed(); + +public: +	void set_stream(const Ref<AudioStream> &p_stream); + +	AudioStreamEditor(); +}; + +class EditorInspectorPluginAudioStream : public EditorInspectorPlugin { +	GDCLASS(EditorInspectorPluginAudioStream, EditorInspectorPlugin); +	AudioStreamEditor *editor = nullptr; + +public: +	virtual bool can_handle(Object *p_object) override; +	virtual void parse_begin(Object *p_object) override; +}; + +class AudioStreamEditorPlugin : public EditorPlugin { +	GDCLASS(AudioStreamEditorPlugin, EditorPlugin); + +public: +	AudioStreamEditorPlugin(); +}; + +#endif // AUDIO_STREAM_EDITOR_PLUGIN_H diff --git a/editor/register_editor_types.cpp b/editor/register_editor_types.cpp index 247d5e1717..8ac0aa6fe3 100644 --- a/editor/register_editor_types.cpp +++ b/editor/register_editor_types.cpp @@ -48,6 +48,7 @@  #include "editor/import/editor_import_plugin.h"  #include "editor/import/resource_importer_scene.h"  #include "editor/plugins/animation_tree_editor_plugin.h" +#include "editor/plugins/audio_stream_editor_plugin.h"  #include "editor/plugins/audio_stream_randomizer_editor_plugin.h"  #include "editor/plugins/bit_map_editor_plugin.h"  #include "editor/plugins/bone_map_editor_plugin.h" @@ -155,6 +156,7 @@ void register_editor_types() {  	// This list is alphabetized, and plugins that depend on Node2D are in their own section below.  	EditorPlugins::add_by_type<AnimationTreeEditorPlugin>(); +	EditorPlugins::add_by_type<AudioStreamEditorPlugin>();  	EditorPlugins::add_by_type<AudioStreamRandomizerEditorPlugin>();  	EditorPlugins::add_by_type<BitMapEditorPlugin>();  	EditorPlugins::add_by_type<BoneMapEditorPlugin>();  |