summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/gdscript/gdscript.cpp6
-rw-r--r--modules/minimp3/audio_stream_mp3.cpp71
-rw-r--r--modules/minimp3/audio_stream_mp3.h25
-rw-r--r--modules/minimp3/doc_classes/AudioStreamMP3.xml6
-rw-r--r--modules/minimp3/resource_importer_mp3.cpp46
-rw-r--r--modules/minimp3/resource_importer_mp3.h6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs8
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs6
-rw-r--r--modules/mono/editor/editor_internal_calls.cpp6
-rw-r--r--modules/ogg/ogg_packet_sequence.cpp2
-rw-r--r--modules/ogg/ogg_packet_sequence.h2
-rw-r--r--modules/openxr/doc_classes/OpenXRInterface.xml4
-rw-r--r--modules/svg/image_loader_svg.cpp4
-rw-r--r--modules/theora/video_stream_theora.h2
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.cpp134
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.h26
-rw-r--r--modules/vorbis/doc_classes/AudioStreamOGGVorbis.xml6
-rw-r--r--modules/vorbis/resource_importer_ogg_vorbis.cpp70
-rw-r--r--modules/vorbis/resource_importer_ogg_vorbis.h7
20 files changed, 379 insertions, 60 deletions
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index e74314389d..964c1133ff 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -870,7 +870,7 @@ Error GDScript::reload(bool p_keep_state) {
}
// TODO: Show all error messages.
_err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_errors().front()->get().line, ("Parse Error: " + parser.get_errors().front()->get().message).utf8().get_data(), false, ERR_HANDLER_SCRIPT);
- ERR_FAIL_V(ERR_PARSE_ERROR);
+ return ERR_PARSE_ERROR;
}
GDScriptAnalyzer analyzer(&parser);
@@ -886,7 +886,7 @@ Error GDScript::reload(bool p_keep_state) {
_err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), e->get().line, ("Parse Error: " + e->get().message).utf8().get_data(), false, ERR_HANDLER_SCRIPT);
e = e->next();
}
- ERR_FAIL_V(ERR_PARSE_ERROR);
+ return ERR_PARSE_ERROR;
}
bool can_run = ScriptServer::is_scripting_enabled() || parser.is_tool();
@@ -904,7 +904,7 @@ Error GDScript::reload(bool p_keep_state) {
GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), compiler.get_error_line(), "Parser Error: " + compiler.get_error());
}
_err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), false, ERR_HANDLER_SCRIPT);
- ERR_FAIL_V(ERR_COMPILATION_FAILED);
+ return ERR_COMPILATION_FAILED;
} else {
return err;
}
diff --git a/modules/minimp3/audio_stream_mp3.cpp b/modules/minimp3/audio_stream_mp3.cpp
index c37bea519f..98bcdea8f4 100644
--- a/modules/minimp3/audio_stream_mp3.cpp
+++ b/modules/minimp3/audio_stream_mp3.cpp
@@ -46,6 +46,12 @@ int AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) {
int frames_mixed_this_step = p_frames;
+ int beat_length_frames = -1;
+ bool beat_loop = mp3_stream->has_loop() && mp3_stream->get_bpm() > 0 && mp3_stream->get_beat_count() > 0;
+ if (beat_loop) {
+ beat_length_frames = mp3_stream->get_beat_count() * mp3_stream->sample_rate * 60 / mp3_stream->get_bpm();
+ }
+
while (todo && active) {
mp3dec_frame_info_t frame_info;
mp3d_sample_t *buf_frame = nullptr;
@@ -54,8 +60,25 @@ int AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) {
if (samples_mixed) {
p_buffer[p_frames - todo] = AudioFrame(buf_frame[0], buf_frame[samples_mixed - 1]);
+ if (loop_fade_remaining < FADE_SIZE) {
+ p_buffer[p_frames - todo] += loop_fade[loop_fade_remaining] * (float(FADE_SIZE - loop_fade_remaining) / float(FADE_SIZE));
+ loop_fade_remaining++;
+ }
--todo;
++frames_mixed;
+
+ if (beat_loop && (int)frames_mixed >= beat_length_frames) {
+ for (int i = 0; i < FADE_SIZE; i++) {
+ samples_mixed = mp3dec_ex_read_frame(mp3d, &buf_frame, &frame_info, mp3_stream->channels);
+ loop_fade[i] = AudioFrame(buf_frame[0], buf_frame[samples_mixed - 1]);
+ if (!samples_mixed) {
+ break;
+ }
+ }
+ loop_fade_remaining = 0;
+ seek(mp3_stream->loop_offset);
+ loops++;
+ }
}
else {
@@ -117,6 +140,10 @@ void AudioStreamPlaybackMP3::seek(float p_time) {
mp3dec_ex_seek(mp3d, (uint64_t)frames_mixed * mp3_stream->channels);
}
+void AudioStreamPlaybackMP3::tag_used_streams() {
+ mp3_stream->tag_used(get_playback_position());
+}
+
AudioStreamPlaybackMP3::~AudioStreamPlaybackMP3() {
if (mp3d) {
mp3dec_ex_close(mp3d);
@@ -124,7 +151,7 @@ AudioStreamPlaybackMP3::~AudioStreamPlaybackMP3() {
}
}
-Ref<AudioStreamPlayback> AudioStreamMP3::instance_playback() {
+Ref<AudioStreamPlayback> AudioStreamMP3::instantiate_playback() {
Ref<AudioStreamPlaybackMP3> mp3s;
ERR_FAIL_COND_V_MSG(data.is_empty(), mp3s,
@@ -206,6 +233,36 @@ bool AudioStreamMP3::is_monophonic() const {
return false;
}
+void AudioStreamMP3::set_bpm(double p_bpm) {
+ ERR_FAIL_COND(p_bpm < 0);
+ bpm = p_bpm;
+ emit_changed();
+}
+
+double AudioStreamMP3::get_bpm() const {
+ return bpm;
+}
+
+void AudioStreamMP3::set_beat_count(int p_beat_count) {
+ ERR_FAIL_COND(p_beat_count < 0);
+ beat_count = p_beat_count;
+ emit_changed();
+}
+
+int AudioStreamMP3::get_beat_count() const {
+ return beat_count;
+}
+
+void AudioStreamMP3::set_bar_beats(int p_bar_beats) {
+ ERR_FAIL_COND(p_bar_beats < 0);
+ bar_beats = p_bar_beats;
+ emit_changed();
+}
+
+int AudioStreamMP3::get_bar_beats() const {
+ return bar_beats;
+}
+
void AudioStreamMP3::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamMP3::set_data);
ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamMP3::get_data);
@@ -216,7 +273,19 @@ void AudioStreamMP3::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_loop_offset", "seconds"), &AudioStreamMP3::set_loop_offset);
ClassDB::bind_method(D_METHOD("get_loop_offset"), &AudioStreamMP3::get_loop_offset);
+ ClassDB::bind_method(D_METHOD("set_bpm", "bpm"), &AudioStreamMP3::set_bpm);
+ ClassDB::bind_method(D_METHOD("get_bpm"), &AudioStreamMP3::get_bpm);
+
+ ClassDB::bind_method(D_METHOD("set_beat_count", "count"), &AudioStreamMP3::set_beat_count);
+ ClassDB::bind_method(D_METHOD("get_beat_count"), &AudioStreamMP3::get_beat_count);
+
+ ClassDB::bind_method(D_METHOD("set_bar_beats", "count"), &AudioStreamMP3::set_bar_beats);
+ ClassDB::bind_method(D_METHOD("get_bar_beats"), &AudioStreamMP3::get_bar_beats);
+
ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_data", "get_data");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bpm", PROPERTY_HINT_RANGE, "0,400,0.01,or_greater"), "set_bpm", "get_bpm");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "beat_count", PROPERTY_HINT_RANGE, "0,512,1,or_greater"), "set_beat_count", "get_beat_count");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "bar_beats", PROPERTY_HINT_RANGE, "2,32,1,or_greater"), "set_bar_beats", "get_bar_beats");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "loop_offset"), "set_loop_offset", "get_loop_offset");
}
diff --git a/modules/minimp3/audio_stream_mp3.h b/modules/minimp3/audio_stream_mp3.h
index c1a60ddccb..428ac1240e 100644
--- a/modules/minimp3/audio_stream_mp3.h
+++ b/modules/minimp3/audio_stream_mp3.h
@@ -41,6 +41,12 @@ class AudioStreamMP3;
class AudioStreamPlaybackMP3 : public AudioStreamPlaybackResampled {
GDCLASS(AudioStreamPlaybackMP3, AudioStreamPlaybackResampled);
+ enum {
+ FADE_SIZE = 256
+ };
+ AudioFrame loop_fade[FADE_SIZE];
+ int loop_fade_remaining = FADE_SIZE;
+
mp3dec_ex_t *mp3d = nullptr;
uint32_t frames_mixed = 0;
bool active = false;
@@ -64,6 +70,8 @@ public:
virtual float get_playback_position() const override;
virtual void seek(float p_time) override;
+ virtual void tag_used_streams() override;
+
AudioStreamPlaybackMP3() {}
~AudioStreamPlaybackMP3();
};
@@ -85,17 +93,30 @@ class AudioStreamMP3 : public AudioStream {
float loop_offset = 0.0;
void clear_data();
+ double bpm = 0;
+ int beat_count = 0;
+ int bar_beats = 4;
+
protected:
static void _bind_methods();
public:
void set_loop(bool p_enable);
- bool has_loop() const;
+ virtual bool has_loop() const override;
void set_loop_offset(float p_seconds);
float get_loop_offset() const;
- virtual Ref<AudioStreamPlayback> instance_playback() override;
+ void set_bpm(double p_bpm);
+ virtual double get_bpm() const override;
+
+ void set_beat_count(int p_beat_count);
+ virtual int get_beat_count() const override;
+
+ void set_bar_beats(int p_bar_beats);
+ virtual int get_bar_beats() const override;
+
+ virtual Ref<AudioStreamPlayback> instantiate_playback() override;
virtual String get_stream_name() const override;
void set_data(const Vector<uint8_t> &p_data);
diff --git a/modules/minimp3/doc_classes/AudioStreamMP3.xml b/modules/minimp3/doc_classes/AudioStreamMP3.xml
index 404f6c31e5..8f03681c06 100644
--- a/modules/minimp3/doc_classes/AudioStreamMP3.xml
+++ b/modules/minimp3/doc_classes/AudioStreamMP3.xml
@@ -9,6 +9,12 @@
<tutorials>
</tutorials>
<members>
+ <member name="bar_beats" type="int" setter="set_bar_beats" getter="get_bar_beats" default="4">
+ </member>
+ <member name="beat_count" type="int" setter="set_beat_count" getter="get_beat_count" default="0">
+ </member>
+ <member name="bpm" type="float" setter="set_bpm" getter="get_bpm" default="0.0">
+ </member>
<member name="data" type="PackedByteArray" setter="set_data" getter="get_data" default="PackedByteArray()">
Contains the audio data in bytes.
You can load a file without having to import it beforehand using the code snippet below. Keep in mind that this snippet loads the whole file into memory and may not be ideal for huge files (hundreds of megabytes or more).
diff --git a/modules/minimp3/resource_importer_mp3.cpp b/modules/minimp3/resource_importer_mp3.cpp
index e03940f963..8526aeef38 100644
--- a/modules/minimp3/resource_importer_mp3.cpp
+++ b/modules/minimp3/resource_importer_mp3.cpp
@@ -34,6 +34,10 @@
#include "core/io/resource_saver.h"
#include "scene/resources/texture.h"
+#ifdef TOOLS_ENABLED
+#include "editor/import/audio_stream_import_settings.h"
+#endif
+
String ResourceImporterMP3::get_importer_name() const {
return "mp3";
}
@@ -69,14 +73,26 @@ String ResourceImporterMP3::get_preset_name(int p_idx) const {
void ResourceImporterMP3::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "loop_offset"), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "bpm", PROPERTY_HINT_RANGE, "0,400,0.01,or_greater"), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "beat_count", PROPERTY_HINT_RANGE, "0,512,or_greater"), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "bar_beats", PROPERTY_HINT_RANGE, "2,32,or_greater"), 4));
}
-Error ResourceImporterMP3::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
- bool loop = p_options["loop"];
- float loop_offset = p_options["loop_offset"];
+#ifdef TOOLS_ENABLED
+bool ResourceImporterMP3::has_advanced_options() const {
+ return true;
+}
+void ResourceImporterMP3::show_advanced_options(const String &p_path) {
+ Ref<AudioStreamMP3> mp3_stream = import_mp3(p_path);
+ if (mp3_stream.is_valid()) {
+ AudioStreamImportSettings::get_singleton()->edit(p_path, "mp3", mp3_stream);
+ }
+}
+#endif
- Ref<FileAccess> f = FileAccess::open(p_source_file, FileAccess::READ);
- ERR_FAIL_COND_V(f.is_null(), ERR_CANT_OPEN);
+Ref<AudioStreamMP3> ResourceImporterMP3::import_mp3(const String &p_path) {
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V(f.is_null(), Ref<AudioStreamMP3>());
uint64_t len = f->get_length();
@@ -90,9 +106,27 @@ Error ResourceImporterMP3::import(const String &p_source_file, const String &p_s
mp3_stream.instantiate();
mp3_stream->set_data(data);
- ERR_FAIL_COND_V(!mp3_stream->get_data().size(), ERR_FILE_CORRUPT);
+ ERR_FAIL_COND_V(!mp3_stream->get_data().size(), Ref<AudioStreamMP3>());
+
+ return mp3_stream;
+}
+
+Error ResourceImporterMP3::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
+ bool loop = p_options["loop"];
+ float loop_offset = p_options["loop_offset"];
+ double bpm = p_options["bpm"];
+ float beat_count = p_options["beat_count"];
+ float bar_beats = p_options["bar_beats"];
+
+ Ref<AudioStreamMP3> mp3_stream = import_mp3(p_source_file);
+ if (mp3_stream.is_null()) {
+ return ERR_CANT_OPEN;
+ }
mp3_stream->set_loop(loop);
mp3_stream->set_loop_offset(loop_offset);
+ mp3_stream->set_bpm(bpm);
+ mp3_stream->set_beat_count(beat_count);
+ mp3_stream->set_bar_beats(bar_beats);
return ResourceSaver::save(p_save_path + ".mp3str", mp3_stream);
}
diff --git a/modules/minimp3/resource_importer_mp3.h b/modules/minimp3/resource_importer_mp3.h
index 678a3773bb..38729d68f8 100644
--- a/modules/minimp3/resource_importer_mp3.h
+++ b/modules/minimp3/resource_importer_mp3.h
@@ -50,6 +50,12 @@ public:
virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override;
virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
+#ifdef TOOLS_ENABLED
+ virtual bool has_advanced_options() const override;
+ virtual void show_advanced_options(const String &p_path) override;
+#endif
+ static Ref<AudioStreamMP3> import_mp3(const String &p_path);
+
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
ResourceImporterMP3();
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
index 6a80e81fdd..e9718cc82c 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
@@ -339,7 +339,7 @@ MONO_AOT_MODE_LAST = 1000,
string MonoLibFile(string libFileName) => libFileName + ".ios.fat.a";
string MonoLibFromTemplate(string libFileName) =>
- Path.Combine(Internal.FullTemplatesDir, "ios-mono-libs", MonoLibFile(libFileName));
+ Path.Combine(Internal.FullExportTemplatesDir, "ios-mono-libs", MonoLibFile(libFileName));
exporter.AddIosProjectStaticLib(MonoLibFromTemplate("libmonosgen-2.0"));
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
index 6a9ead9aa1..cca18a2a1f 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
@@ -337,7 +337,7 @@ namespace GodotTools.Export
string TemplateDirName() => $"data.mono.{platform}.{bits}.{target}";
- string templateDirPath = Path.Combine(Internal.FullTemplatesDir, TemplateDirName());
+ string templateDirPath = Path.Combine(Internal.FullExportTemplatesDir, TemplateDirName());
bool validTemplatePathFound = true;
if (!Directory.Exists(templateDirPath))
@@ -347,7 +347,7 @@ namespace GodotTools.Export
if (isDebug)
{
target = "debug"; // Support both 'release_debug' and 'debug' for the template data directory name
- templateDirPath = Path.Combine(Internal.FullTemplatesDir, TemplateDirName());
+ templateDirPath = Path.Combine(Internal.FullExportTemplatesDir, TemplateDirName());
validTemplatePathFound = true;
if (!Directory.Exists(templateDirPath))
@@ -398,13 +398,13 @@ namespace GodotTools.Export
private static string GetBclProfileDir(string profile)
{
- string templatesDir = Internal.FullTemplatesDir;
+ string templatesDir = Internal.FullExportTemplatesDir;
return Path.Combine(templatesDir, "bcl", profile);
}
private static string DeterminePlatformBclDir(string platform)
{
- string templatesDir = Internal.FullTemplatesDir;
+ string templatesDir = Internal.FullExportTemplatesDir;
string platformBclDir = Path.Combine(templatesDir, "bcl", platform);
if (!File.Exists(Path.Combine(platformBclDir, "mscorlib.dll")))
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
index 72985db292..12c90178c9 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
@@ -12,8 +12,8 @@ namespace GodotTools.Internals
public static string UpdateApiAssembliesFromPrebuilt(string config) =>
internal_UpdateApiAssembliesFromPrebuilt(config);
- public static string FullTemplatesDir =>
- internal_FullTemplatesDir();
+ public static string FullExportTemplatesDir =>
+ internal_FullExportTemplatesDir();
public static string SimplifyGodotPath(this string path) => internal_SimplifyGodotPath(path);
@@ -57,7 +57,7 @@ namespace GodotTools.Internals
private static extern string internal_UpdateApiAssembliesFromPrebuilt(string config);
[MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_FullTemplatesDir();
+ private static extern string internal_FullExportTemplatesDir();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_SimplifyGodotPath(this string path);
diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp
index 5bf3839e94..5e4fe90553 100644
--- a/modules/mono/editor/editor_internal_calls.cpp
+++ b/modules/mono/editor/editor_internal_calls.cpp
@@ -188,8 +188,8 @@ MonoString *godot_icall_Internal_UpdateApiAssembliesFromPrebuilt(MonoString *p_c
return GDMonoMarshal::mono_string_from_godot(error_str);
}
-MonoString *godot_icall_Internal_FullTemplatesDir() {
- String full_templates_dir = EditorSettings::get_singleton()->get_templates_dir().plus_file(VERSION_FULL_CONFIG);
+MonoString *godot_icall_Internal_FullExportTemplatesDir() {
+ String full_templates_dir = EditorSettings::get_singleton()->get_export_templates_dir().plus_file(VERSION_FULL_CONFIG);
return GDMonoMarshal::mono_string_from_godot(full_templates_dir);
}
@@ -364,7 +364,7 @@ void register_editor_internal_calls() {
// Internals
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_UpdateApiAssembliesFromPrebuilt", godot_icall_Internal_UpdateApiAssembliesFromPrebuilt);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_FullTemplatesDir", godot_icall_Internal_FullTemplatesDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_FullExportTemplatesDir", godot_icall_Internal_FullExportTemplatesDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_SimplifyGodotPath", godot_icall_Internal_SimplifyGodotPath);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_IsMacOSAppBundleInstalled", godot_icall_Internal_IsMacOSAppBundleInstalled);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotIs32Bits", godot_icall_Internal_GodotIs32Bits);
diff --git a/modules/ogg/ogg_packet_sequence.cpp b/modules/ogg/ogg_packet_sequence.cpp
index da52ecfdd5..e18fa9e590 100644
--- a/modules/ogg/ogg_packet_sequence.cpp
+++ b/modules/ogg/ogg_packet_sequence.cpp
@@ -106,7 +106,7 @@ float OGGPacketSequence::get_length() const {
return granule_pos / sampling_rate;
}
-Ref<OGGPacketSequencePlayback> OGGPacketSequence::instance_playback() {
+Ref<OGGPacketSequencePlayback> OGGPacketSequence::instantiate_playback() {
Ref<OGGPacketSequencePlayback> playback;
playback.instantiate();
playback->ogg_packet_sequence = Ref<OGGPacketSequence>(this);
diff --git a/modules/ogg/ogg_packet_sequence.h b/modules/ogg/ogg_packet_sequence.h
index 73e3cb4fff..76b673c6ac 100644
--- a/modules/ogg/ogg_packet_sequence.h
+++ b/modules/ogg/ogg_packet_sequence.h
@@ -86,7 +86,7 @@ public:
// Returns the granule position of the last page in this sequence.
int64_t get_final_granule_pos() const;
- Ref<OGGPacketSequencePlayback> instance_playback();
+ Ref<OGGPacketSequencePlayback> instantiate_playback();
OGGPacketSequence() {}
virtual ~OGGPacketSequence() {}
diff --git a/modules/openxr/doc_classes/OpenXRInterface.xml b/modules/openxr/doc_classes/OpenXRInterface.xml
index 74f708bc95..25bf496de9 100644
--- a/modules/openxr/doc_classes/OpenXRInterface.xml
+++ b/modules/openxr/doc_classes/OpenXRInterface.xml
@@ -5,10 +5,10 @@
</brief_description>
<description>
The OpenXR interface allows Godot to interact with OpenXR runtimes and make it possible to create XR experiences and games.
- Due to the needs of OpenXR this interface works slightly different then other plugin based XR interfaces. It needs to be initialised when Godot starts. You need to enable OpenXR, settings for this can be found in your games project settings under the XR heading. You do need to mark a viewport for use with XR in order for Godot to know which render result should be output to the headset.
+ Due to the needs of OpenXR this interface works slightly different than other plugin based XR interfaces. It needs to be initialised when Godot starts. You need to enable OpenXR, settings for this can be found in your games project settings under the XR heading. You do need to mark a viewport for use with XR in order for Godot to know which render result should be output to the headset.
</description>
<tutorials>
- <link title="OpenXR documentation">$DOCS_URL/tutorials/vr/openxr/index.html</link>
+ <link title="Setting up XR">$DOCS_URL/tutorials/xr/setting_up_xr.html</link>
</tutorials>
<signals>
<signal name="pose_recentered">
diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp
index dcb1f1d744..87e2fae2d0 100644
--- a/modules/svg/image_loader_svg.cpp
+++ b/modules/svg/image_loader_svg.cpp
@@ -80,8 +80,8 @@ void ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, String p_strin
float fw, fh;
picture->size(&fw, &fh);
- uint32_t width = MIN(fw * p_scale, 16 * 1024);
- uint32_t height = MIN(fh * p_scale, 16 * 1024);
+ uint32_t width = MIN(round(fw * p_scale), 16 * 1024);
+ uint32_t height = MIN(round(fh * p_scale), 16 * 1024);
picture->size(width, height);
std::unique_ptr<tvg::SwCanvas> sw_canvas = tvg::SwCanvas::gen();
diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h
index 8940ed6aff..dd69bf066a 100644
--- a/modules/theora/video_stream_theora.h
+++ b/modules/theora/video_stream_theora.h
@@ -170,7 +170,7 @@ protected:
static void _bind_methods();
public:
- Ref<VideoStreamPlayback> instance_playback() override {
+ Ref<VideoStreamPlayback> instantiate_playback() override {
Ref<VideoStreamPlaybackTheora> pb = memnew(VideoStreamPlaybackTheora);
pb->set_audio_track(audio_track);
pb->set_file(file);
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp
index 89a6b03ff8..76f7317daa 100644
--- a/modules/vorbis/audio_stream_ogg_vorbis.cpp
+++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp
@@ -43,28 +43,93 @@ int AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_fram
int todo = p_frames;
- int start_buffer = 0;
+ int beat_length_frames = -1;
+ bool beat_loop = vorbis_stream->has_loop();
+ if (beat_loop && vorbis_stream->get_bpm() > 0 && vorbis_stream->get_beat_count() > 0) {
+ beat_length_frames = vorbis_stream->get_beat_count() * vorbis_data->get_sampling_rate() * 60 / vorbis_stream->get_bpm();
+ }
while (todo > 0 && active) {
AudioFrame *buffer = p_buffer;
- if (start_buffer > 0) {
- buffer = buffer + start_buffer;
+ buffer += p_frames - todo;
+
+ int to_mix = todo;
+ if (beat_length_frames >= 0 && (beat_length_frames - (int)frames_mixed) < to_mix) {
+ to_mix = MAX(0, beat_length_frames - (int)frames_mixed);
}
- int mixed = _mix_frames_vorbis(buffer, todo);
+
+ int mixed = _mix_frames_vorbis(buffer, to_mix);
ERR_FAIL_COND_V(mixed < 0, 0);
todo -= mixed;
frames_mixed += mixed;
- start_buffer += mixed;
+
+ if (loop_fade_remaining < FADE_SIZE) {
+ int to_fade = loop_fade_remaining + MIN(FADE_SIZE - loop_fade_remaining, mixed);
+ for (int i = loop_fade_remaining; i < to_fade; i++) {
+ buffer[i - loop_fade_remaining] += loop_fade[i] * (float(FADE_SIZE - i) / float(FADE_SIZE));
+ }
+ loop_fade_remaining = to_fade;
+ }
+
+ if (beat_length_frames >= 0) {
+ /**
+ * Length determined by beat length
+ * This code is commented out because, in practice, it is prefered that the fade
+ * is done by the transitioner and this stream just goes on until it ends while fading out.
+ *
+ * End fade implementation is left here for reference in case at some point this feature
+ * is desired.
+
+ if (!beat_loop && (int)frames_mixed > beat_length_frames - FADE_SIZE) {
+ print_line("beat length fade/after mix?");
+ //No loop, just fade and finish
+ for (int i = 0; i < mixed; i++) {
+ int idx = frames_mixed + i - mixed;
+ buffer[i] *= 1.0 - float(MAX(0, (idx - (beat_length_frames - FADE_SIZE)))) / float(FADE_SIZE);
+ }
+ if ((int)frames_mixed == beat_length_frames) {
+ for (int i = p_frames - todo; i < p_frames; i++) {
+ p_buffer[i] = AudioFrame(0, 0);
+ }
+ active = false;
+ break;
+ }
+ } else
+ **/
+
+ if (beat_loop && beat_length_frames <= (int)frames_mixed) {
+ // End of file when doing beat-based looping. <= used instead of == because importer editing
+ if (!have_packets_left && !have_samples_left) {
+ //Nothing remaining, so do nothing.
+ loop_fade_remaining = FADE_SIZE;
+ } else {
+ // Add some loop fade;
+ int faded_mix = _mix_frames_vorbis(loop_fade, FADE_SIZE);
+
+ for (int i = faded_mix; i < FADE_SIZE; i++) {
+ // In case lesss was mixed, pad with zeros
+ loop_fade[i] = AudioFrame(0, 0);
+ }
+ loop_fade_remaining = 0;
+ }
+
+ seek(vorbis_stream->loop_offset);
+ loops++;
+ // We still have buffer to fill, start from this element in the next iteration.
+ continue;
+ }
+ }
+
if (!have_packets_left && !have_samples_left) {
- //end of file!
+ // Actual end of file!
bool is_not_empty = mixed > 0 || vorbis_stream->get_length() > 0;
if (vorbis_stream->loop && is_not_empty) {
//loop
seek(vorbis_stream->loop_offset);
loops++;
- // we still have buffer to fill, start from this element in the next iteration.
- start_buffer = p_frames - todo;
+ // We still have buffer to fill, start from this element in the next iteration.
+
} else {
for (int i = p_frames - todo; i < p_frames; i++) {
p_buffer[i] = AudioFrame(0, 0);
@@ -130,7 +195,7 @@ bool AudioStreamPlaybackOGGVorbis::_alloc_vorbis() {
comment_is_allocated = true;
ERR_FAIL_COND_V(vorbis_data.is_null(), false);
- vorbis_data_playback = vorbis_data->instance_playback();
+ vorbis_data_playback = vorbis_data->instantiate_playback();
ogg_packet *packet;
int err;
@@ -160,6 +225,7 @@ bool AudioStreamPlaybackOGGVorbis::_alloc_vorbis() {
void AudioStreamPlaybackOGGVorbis::start(float p_from_pos) {
ERR_FAIL_COND(!ready);
+ loop_fade_remaining = FADE_SIZE;
active = true;
seek(p_from_pos);
loops = 0;
@@ -182,6 +248,10 @@ float AudioStreamPlaybackOGGVorbis::get_playback_position() const {
return float(frames_mixed) / vorbis_data->get_sampling_rate();
}
+void AudioStreamPlaybackOGGVorbis::tag_used_streams() {
+ vorbis_stream->tag_used(get_playback_position());
+}
+
void AudioStreamPlaybackOGGVorbis::seek(float p_time) {
ERR_FAIL_COND(!ready);
ERR_FAIL_COND(vorbis_stream.is_null());
@@ -315,7 +385,7 @@ AudioStreamPlaybackOGGVorbis::~AudioStreamPlaybackOGGVorbis() {
}
}
-Ref<AudioStreamPlayback> AudioStreamOGGVorbis::instance_playback() {
+Ref<AudioStreamPlayback> AudioStreamOGGVorbis::instantiate_playback() {
Ref<AudioStreamPlaybackOGGVorbis> ovs;
ERR_FAIL_COND_V(packet_sequence.is_null(), nullptr);
@@ -347,7 +417,7 @@ void AudioStreamOGGVorbis::maybe_update_info() {
vorbis_info_init(&info);
vorbis_comment_init(&comment);
- Ref<OGGPacketSequencePlayback> packet_sequence_playback = packet_sequence->instance_playback();
+ Ref<OGGPacketSequencePlayback> packet_sequence_playback = packet_sequence->instantiate_playback();
for (int i = 0; i < 3; i++) {
ogg_packet *packet;
@@ -405,6 +475,36 @@ float AudioStreamOGGVorbis::get_length() const {
return packet_sequence->get_length();
}
+void AudioStreamOGGVorbis::set_bpm(double p_bpm) {
+ ERR_FAIL_COND(p_bpm < 0);
+ bpm = p_bpm;
+ emit_changed();
+}
+
+double AudioStreamOGGVorbis::get_bpm() const {
+ return bpm;
+}
+
+void AudioStreamOGGVorbis::set_beat_count(int p_beat_count) {
+ ERR_FAIL_COND(p_beat_count < 0);
+ beat_count = p_beat_count;
+ emit_changed();
+}
+
+int AudioStreamOGGVorbis::get_beat_count() const {
+ return beat_count;
+}
+
+void AudioStreamOGGVorbis::set_bar_beats(int p_bar_beats) {
+ ERR_FAIL_COND(p_bar_beats < 2);
+ bar_beats = p_bar_beats;
+ emit_changed();
+}
+
+int AudioStreamOGGVorbis::get_bar_beats() const {
+ return bar_beats;
+}
+
bool AudioStreamOGGVorbis::is_monophonic() const {
return false;
}
@@ -419,7 +519,19 @@ void AudioStreamOGGVorbis::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_loop_offset", "seconds"), &AudioStreamOGGVorbis::set_loop_offset);
ClassDB::bind_method(D_METHOD("get_loop_offset"), &AudioStreamOGGVorbis::get_loop_offset);
+ ClassDB::bind_method(D_METHOD("set_bpm", "bpm"), &AudioStreamOGGVorbis::set_bpm);
+ ClassDB::bind_method(D_METHOD("get_bpm"), &AudioStreamOGGVorbis::get_bpm);
+
+ ClassDB::bind_method(D_METHOD("set_beat_count", "count"), &AudioStreamOGGVorbis::set_beat_count);
+ ClassDB::bind_method(D_METHOD("get_beat_count"), &AudioStreamOGGVorbis::get_beat_count);
+
+ ClassDB::bind_method(D_METHOD("set_bar_beats", "count"), &AudioStreamOGGVorbis::set_bar_beats);
+ ClassDB::bind_method(D_METHOD("get_bar_beats"), &AudioStreamOGGVorbis::get_bar_beats);
+
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "packet_sequence", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_packet_sequence", "get_packet_sequence");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bpm", PROPERTY_HINT_RANGE, "0,400,0.01,or_greater"), "set_bpm", "get_bpm");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "beat_count", PROPERTY_HINT_RANGE, "0,512,1,or_greater"), "set_beat_count", "get_beat_count");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "bar_beats", PROPERTY_HINT_RANGE, "2,32,1,or_greater"), "set_bar_beats", "get_bar_beats");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "loop_offset"), "set_loop_offset", "get_loop_offset");
}
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.h b/modules/vorbis/audio_stream_ogg_vorbis.h
index b09ef9ff5d..22c2eb4d73 100644
--- a/modules/vorbis/audio_stream_ogg_vorbis.h
+++ b/modules/vorbis/audio_stream_ogg_vorbis.h
@@ -45,6 +45,12 @@ class AudioStreamPlaybackOGGVorbis : public AudioStreamPlaybackResampled {
bool active = false;
int loops = 0;
+ enum {
+ FADE_SIZE = 256
+ };
+ AudioFrame loop_fade[FADE_SIZE];
+ int loop_fade_remaining = FADE_SIZE;
+
vorbis_info info;
vorbis_comment comment;
vorbis_dsp_state dsp_state;
@@ -66,6 +72,7 @@ class AudioStreamPlaybackOGGVorbis : public AudioStreamPlaybackResampled {
Ref<OGGPacketSequencePlayback> vorbis_data_playback;
Ref<AudioStreamOGGVorbis> vorbis_stream;
+ int _mix_frames(AudioFrame *p_buffer, int p_frames);
int _mix_frames_vorbis(AudioFrame *p_buffer, int p_frames);
// Allocates vorbis data structures. Returns true upon success, false on failure.
@@ -85,6 +92,8 @@ public:
virtual float get_playback_position() const override;
virtual void seek(float p_time) override;
+ virtual void tag_used_streams() override;
+
AudioStreamPlaybackOGGVorbis() {}
~AudioStreamPlaybackOGGVorbis();
};
@@ -107,17 +116,30 @@ class AudioStreamOGGVorbis : public AudioStream {
Ref<OGGPacketSequence> packet_sequence;
+ double bpm = 0;
+ int beat_count = 0;
+ int bar_beats = 4;
+
protected:
static void _bind_methods();
public:
void set_loop(bool p_enable);
- bool has_loop() const;
+ virtual bool has_loop() const override;
void set_loop_offset(float p_seconds);
float get_loop_offset() const;
- virtual Ref<AudioStreamPlayback> instance_playback() override;
+ void set_bpm(double p_bpm);
+ virtual double get_bpm() const override;
+
+ void set_beat_count(int p_beat_count);
+ virtual int get_beat_count() const override;
+
+ void set_bar_beats(int p_bar_beats);
+ virtual int get_bar_beats() const override;
+
+ virtual Ref<AudioStreamPlayback> instantiate_playback() override;
virtual String get_stream_name() const override;
void set_packet_sequence(Ref<OGGPacketSequence> p_packet_sequence);
diff --git a/modules/vorbis/doc_classes/AudioStreamOGGVorbis.xml b/modules/vorbis/doc_classes/AudioStreamOGGVorbis.xml
index 2f210a6cb4..f87296dcd8 100644
--- a/modules/vorbis/doc_classes/AudioStreamOGGVorbis.xml
+++ b/modules/vorbis/doc_classes/AudioStreamOGGVorbis.xml
@@ -7,6 +7,12 @@
<tutorials>
</tutorials>
<members>
+ <member name="bar_beats" type="int" setter="set_bar_beats" getter="get_bar_beats" default="4">
+ </member>
+ <member name="beat_count" type="int" setter="set_beat_count" getter="get_beat_count" default="0">
+ </member>
+ <member name="bpm" type="float" setter="set_bpm" getter="get_bpm" default="0.0">
+ </member>
<member name="loop" type="bool" setter="set_loop" getter="has_loop" default="false">
If [code]true[/code], the stream will automatically loop when it reaches the end.
</member>
diff --git a/modules/vorbis/resource_importer_ogg_vorbis.cpp b/modules/vorbis/resource_importer_ogg_vorbis.cpp
index 7ee6446313..9461d531fd 100644
--- a/modules/vorbis/resource_importer_ogg_vorbis.cpp
+++ b/modules/vorbis/resource_importer_ogg_vorbis.cpp
@@ -30,13 +30,16 @@
#include "resource_importer_ogg_vorbis.h"
-#include "audio_stream_ogg_vorbis.h"
#include "core/io/file_access.h"
#include "core/io/resource_saver.h"
#include "scene/resources/texture.h"
#include "thirdparty/libogg/ogg/ogg.h"
#include "thirdparty/libvorbis/vorbis/codec.h"
+#ifdef TOOLS_ENABLED
+#include "editor/import/audio_stream_import_settings.h"
+#endif
+
String ResourceImporterOGGVorbis::get_importer_name() const {
return "oggvorbisstr";
}
@@ -72,14 +75,14 @@ String ResourceImporterOGGVorbis::get_preset_name(int p_idx) const {
void ResourceImporterOGGVorbis::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "loop_offset"), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "bpm", PROPERTY_HINT_RANGE, "0,400,0.01,or_greater"), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "beat_count", PROPERTY_HINT_RANGE, "0,512,or_greater"), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "bar_beats", PROPERTY_HINT_RANGE, "2,32,or_greater"), 4));
}
-Error ResourceImporterOGGVorbis::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
- bool loop = p_options["loop"];
- float loop_offset = p_options["loop_offset"];
-
- Ref<FileAccess> f = FileAccess::open(p_source_file, FileAccess::READ);
- ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, "Cannot open file '" + p_source_file + "'.");
+Ref<AudioStreamOGGVorbis> ResourceImporterOGGVorbis::import_ogg_vorbis(const String &p_path) {
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(f.is_null(), Ref<AudioStreamOGGVorbis>(), "Cannot open file '" + p_path + "'.");
uint64_t len = f->get_length();
@@ -107,16 +110,16 @@ Error ResourceImporterOGGVorbis::import(const String &p_source_file, const Strin
size_t packet_count = 0;
bool done = false;
while (!done) {
- ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Error::ERR_INVALID_DATA, "Ogg sync error " + itos(err));
+ ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Ref<AudioStreamOGGVorbis>(), "Ogg sync error " + itos(err));
while (ogg_sync_pageout(&sync_state, &page) != 1) {
if (cursor >= len) {
done = true;
break;
}
- ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Error::ERR_INVALID_DATA, "Ogg sync error " + itos(err));
+ ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Ref<AudioStreamOGGVorbis>(), "Ogg sync error " + itos(err));
char *sync_buf = ogg_sync_buffer(&sync_state, OGG_SYNC_BUFFER_SIZE);
- ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Error::ERR_INVALID_DATA, "Ogg sync error " + itos(err));
- ERR_FAIL_COND_V(cursor > len, Error::ERR_INVALID_DATA);
+ ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Ref<AudioStreamOGGVorbis>(), "Ogg sync error " + itos(err));
+ ERR_FAIL_COND_V(cursor > len, Ref<AudioStreamOGGVorbis>());
size_t copy_size = len - cursor;
if (copy_size > OGG_SYNC_BUFFER_SIZE) {
copy_size = OGG_SYNC_BUFFER_SIZE;
@@ -124,22 +127,22 @@ Error ResourceImporterOGGVorbis::import(const String &p_source_file, const Strin
memcpy(sync_buf, &file_data[cursor], copy_size);
ogg_sync_wrote(&sync_state, copy_size);
cursor += copy_size;
- ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Error::ERR_INVALID_DATA, "Ogg sync error " + itos(err));
+ ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Ref<AudioStreamOGGVorbis>(), "Ogg sync error " + itos(err));
}
if (done) {
break;
}
- ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Error::ERR_INVALID_DATA, "Ogg sync error " + itos(err));
+ ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Ref<AudioStreamOGGVorbis>(), "Ogg sync error " + itos(err));
// Have a page now.
if (!initialized_stream) {
if (ogg_stream_init(&stream_state, ogg_page_serialno(&page))) {
- ERR_FAIL_V_MSG(Error::ERR_OUT_OF_MEMORY, "Failed allocating memory for OGG Vorbis stream.");
+ ERR_FAIL_V_MSG(Ref<AudioStreamOGGVorbis>(), "Failed allocating memory for OGG Vorbis stream.");
}
initialized_stream = true;
}
ogg_stream_pagein(&stream_state, &page);
- ERR_FAIL_COND_V_MSG((err = ogg_stream_check(&stream_state)), Error::ERR_INVALID_DATA, "Ogg stream error " + itos(err));
+ ERR_FAIL_COND_V_MSG((err = ogg_stream_check(&stream_state)), Ref<AudioStreamOGGVorbis>(), "Ogg stream error " + itos(err));
int desync_iters = 0;
Vector<Vector<uint8_t>> packet_data;
@@ -150,7 +153,7 @@ Error ResourceImporterOGGVorbis::import(const String &p_source_file, const Strin
if (err == -1) {
// According to the docs this is usually recoverable, but don't sit here spinning forever.
desync_iters++;
- ERR_FAIL_COND_V_MSG(desync_iters > 100, Error::ERR_INVALID_DATA, "Packet sync issue during ogg import");
+ ERR_FAIL_COND_V_MSG(desync_iters > 100, Ref<AudioStreamOGGVorbis>(), "Packet sync issue during ogg import");
continue;
} else if (err == 0) {
// Not enough data to fully reconstruct a packet. Go on to the next page.
@@ -183,12 +186,45 @@ Error ResourceImporterOGGVorbis::import(const String &p_source_file, const Strin
ogg_sync_clear(&sync_state);
if (ogg_packet_sequence->get_packet_granule_positions().is_empty()) {
- ERR_FAIL_V_MSG(Error::ERR_FILE_CORRUPT, "OGG Vorbis decoding failed. Check that your data is a valid OGG Vorbis audio stream.");
+ ERR_FAIL_V_MSG(Ref<AudioStreamOGGVorbis>(), "OGG Vorbis decoding failed. Check that your data is a valid OGG Vorbis audio stream.");
}
ogg_vorbis_stream->set_packet_sequence(ogg_packet_sequence);
+
+ return ogg_vorbis_stream;
+}
+
+#ifdef TOOLS_ENABLED
+
+bool ResourceImporterOGGVorbis::has_advanced_options() const {
+ return true;
+}
+
+void ResourceImporterOGGVorbis::show_advanced_options(const String &p_path) {
+ Ref<AudioStreamOGGVorbis> ogg_stream = import_ogg_vorbis(p_path);
+ if (ogg_stream.is_valid()) {
+ AudioStreamImportSettings::get_singleton()->edit(p_path, "oggvorbisstr", ogg_stream);
+ }
+}
+#endif
+
+Error ResourceImporterOGGVorbis::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
+ bool loop = p_options["loop"];
+ float loop_offset = p_options["loop_offset"];
+ double bpm = p_options["bpm"];
+ int beat_count = p_options["beat_count"];
+ int bar_beats = p_options["bar_beats"];
+
+ Ref<AudioStreamOGGVorbis> ogg_vorbis_stream = import_ogg_vorbis(p_source_file);
+ if (ogg_vorbis_stream.is_null()) {
+ return ERR_CANT_OPEN;
+ }
+
ogg_vorbis_stream->set_loop(loop);
ogg_vorbis_stream->set_loop_offset(loop_offset);
+ ogg_vorbis_stream->set_bpm(bpm);
+ ogg_vorbis_stream->set_beat_count(beat_count);
+ ogg_vorbis_stream->set_bar_beats(bar_beats);
return ResourceSaver::save(p_save_path + ".oggvorbisstr", ogg_vorbis_stream);
}
diff --git a/modules/vorbis/resource_importer_ogg_vorbis.h b/modules/vorbis/resource_importer_ogg_vorbis.h
index 3b4a68a1fd..e6e98a29c1 100644
--- a/modules/vorbis/resource_importer_ogg_vorbis.h
+++ b/modules/vorbis/resource_importer_ogg_vorbis.h
@@ -31,6 +31,7 @@
#ifndef RESOURCE_IMPORTER_OGG_VORBIS_H
#define RESOURCE_IMPORTER_OGG_VORBIS_H
+#include "audio_stream_ogg_vorbis.h"
#include "core/io/resource_importer.h"
class ResourceImporterOGGVorbis : public ResourceImporter {
@@ -43,7 +44,13 @@ class ResourceImporterOGGVorbis : public ResourceImporter {
private:
// virtual int get_samples_in_packet(Vector<uint8_t> p_packet) = 0;
+ static Ref<AudioStreamOGGVorbis> import_ogg_vorbis(const String &p_path);
+
public:
+#ifdef TOOLS_ENABLED
+ virtual bool has_advanced_options() const override;
+ virtual void show_advanced_options(const String &p_path) override;
+#endif
virtual void get_recognized_extensions(List<String> *p_extensions) const override;
virtual String get_save_extension() const override;
virtual String get_resource_type() const override;