diff --git a/README.md b/README.md index 5bd54ec..c952044 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Just go to the prebuilt branch and use the download ZIP feature of Github, extra If you want to take a look at a commit that's more recent, then you can download the addon as a CI build artifact from the Github workflow action. -Unfortunately there are no MacOS, Android, iOS, or any 32-bit binaries currently prebuilt. If you need any of those you will have to build them yourself (and in the case of mobile platforms, add them to the .gdextension file) +Unfortunately there are no MacOS, Android, iOS, or any 32-bit binaries currently prebuilt. If you need any of those you will have to build them yourself and add them to the .gdextension file. ## Documentation diff --git a/addons/godot-openmpt/mpt_importer.gd b/addons/godot-openmpt/mpt_importer.gd index a7af69b..a072da6 100644 --- a/addons/godot-openmpt/mpt_importer.gd +++ b/addons/godot-openmpt/mpt_importer.gd @@ -34,6 +34,16 @@ func _get_import_options(path, preset_index) -> Array[Dictionary]: "property_hint": PROPERTY_HINT_ENUM, "hint_string": "Disabled,Enabled", "default_value": 0 + }, + { + "name": "load/skip_plugins", + "type": TYPE_BOOL, + "default_value": false + }, + { + "name": "load/skip_subsongs_init", + "type": TYPE_BOOL, + "default_value": false }] func _get_option_visibility(path, option_name, options) -> bool: @@ -57,6 +67,8 @@ func _import(source_file, save_path, options, r_platform_variants, r_gen_files) return ERR_PARSE_ERROR var stream = AudioStreamMPT.new() + stream.skip_plugins = options["load/skip_plugins"] + stream.skip_subsongs_init = options["load/skip_subsongs_init"] stream.data = file_data if stream.data.is_empty(): return stream.get_module_error() diff --git a/addons/godot-openmpt/openmpt.gdextension b/addons/godot-openmpt/openmpt.gdextension index 459a638..4b1a16b 100644 --- a/addons/godot-openmpt/openmpt.gdextension +++ b/addons/godot-openmpt/openmpt.gdextension @@ -7,11 +7,7 @@ compatibility_minimum = "4.2" macos.debug = "bin/libgdmpt-macos.debug.64.framework" macos.release = "bin/libgdmpt-macos.release.64.framework" -windows.debug.x86_32 = "bin/libgdmpt-windows.debug.32.dll" -windows.release.x86_32 = "bin/libgdmpt-windows.release.32.dll" windows.debug.x86_64 = "bin/libgdmpt-windows.debug.64.dll" windows.release.x86_64 = "bin/libgdmpt-windows.release.64.dll" -linux.debug.x86_32 = "bin/libgdmpt-linux.debug.32.so" -linux.release.x86_32 = "bin/libgdmpt-linux.release.32.so" linux.debug.x86_64 = "bin/libgdmpt-linux.debug.64.so" linux.release.x86_64 = "bin/libgdmpt-linux.release.64.so" diff --git a/addons/godot-openmpt/plugin.cfg b/addons/godot-openmpt/plugin.cfg index fcd185d..d0c2bcb 100644 --- a/addons/godot-openmpt/plugin.cfg +++ b/addons/godot-openmpt/plugin.cfg @@ -3,5 +3,5 @@ name="Godot OpenMPT" description="Integrates OpenMPT as a playable AudioStream" author="Dudejoe870" -version="1.0.0" +version="1.3" script="mpt_import.gd" diff --git a/src/audio_stream_mpt.cpp b/src/audio_stream_mpt.cpp index 9027478..ad65ef2 100644 --- a/src/audio_stream_mpt.cpp +++ b/src/audio_stream_mpt.cpp @@ -19,13 +19,13 @@ using namespace godot; #define CHECK_MOD_LOADED_RETV() if (!this->mpt_module) { WARN_PRINT_ED(MOD_NOT_LOADED_MSG); return; } #define CHECK_INT_LOADED_RET(retval) CHECK_MOD_LOADED_RET(retval); if (!this->mpt_interactive) { WARN_PRINT_ED(INT_NOT_LOADED_MSG); return retval; } -#define CHECK_INT_LOADED_RETV() CHECK_MOD_LOADED_RETV(); if (!this->mpt_module) { WARN_PRINT_ED(INT_NOT_LOADED_MSG); return; } +#define CHECK_INT_LOADED_RETV() CHECK_MOD_LOADED_RETV(); if (!this->mpt_interactive) { WARN_PRINT_ED(INT_NOT_LOADED_MSG); return; } -#define CHECK_INT2_LOADED_RET(retval) CHECK_MOD_LOADED_RET(retval); if (!this->mpt_interactive) { WARN_PRINT_ED(INT2_NOT_LOADED_MSG); return retval; } -#define CHECK_INT2_LOADED_RETV() CHECK_MOD_LOADED_RETV(); if (!this->mpt_module) { WARN_PRINT_ED(INT2_NOT_LOADED_MSG); return; } +#define CHECK_INT2_LOADED_RET(retval) CHECK_MOD_LOADED_RET(retval); if (!this->mpt_interactive2) { WARN_PRINT_ED(INT2_NOT_LOADED_MSG); return retval; } +#define CHECK_INT2_LOADED_RETV() CHECK_MOD_LOADED_RETV(); if (!this->mpt_interactive2) { WARN_PRINT_ED(INT2_NOT_LOADED_MSG); return; } -#define CHECK_INT3_LOADED_RET(retval) CHECK_MOD_LOADED_RET(retval); if (!this->mpt_interactive) { WARN_PRINT_ED(INT3_NOT_LOADED_MSG); return retval; } -#define CHECK_INT3_LOADED_RETV() CHECK_MOD_LOADED_RETV(); if (!this->mpt_module) { WARN_PRINT_ED(INT3_NOT_LOADED_MSG); return; } +#define CHECK_INT3_LOADED_RET(retval) CHECK_MOD_LOADED_RET(retval); if (!this->mpt_interactive3) { WARN_PRINT_ED(INT3_NOT_LOADED_MSG); return retval; } +#define CHECK_INT3_LOADED_RETV() CHECK_MOD_LOADED_RETV(); if (!this->mpt_interactive3) { WARN_PRINT_ED(INT3_NOT_LOADED_MSG); return; } void AudioStreamPlaybackMPT::_start(double p_from_pos) { _seek(p_from_pos); @@ -285,6 +285,16 @@ double AudioStreamPlaybackMPT::get_note_finetune(int32_t channel) const { return this->mpt_interactive2->get_note_finetune(channel); } +void AudioStreamPlaybackMPT::set_sync_samples(bool p_enable) { + CHECK_MOD_LOADED_RETV(); + this->mpt_module->ctl_set_boolean("seek.sync_samples", p_enable); +} + +bool AudioStreamPlaybackMPT::get_sync_samples() const { + CHECK_MOD_LOADED_RET(true); + return this->mpt_module->ctl_get_boolean("seek.sync_samples"); +} + int32_t AudioStreamPlaybackMPT::_mix(AudioFrame *p_buffer, double p_rate_scale, int32_t p_frames) { if (!this->mpt_module) { active = false; @@ -382,6 +392,9 @@ void AudioStreamPlaybackMPT::_bind_methods() { ClassDB::bind_method(D_METHOD("set_note_finetune", "channel", "finetune"), &AudioStreamPlaybackMPT::set_note_finetune); ClassDB::bind_method(D_METHOD("get_note_finetune", "channel"), &AudioStreamPlaybackMPT::get_note_finetune); + + ClassDB::bind_method(D_METHOD("set_sync_samples", "sync_samples"), &AudioStreamPlaybackMPT::set_sync_samples); + ClassDB::bind_method(D_METHOD("get_sync_samples"), &AudioStreamPlaybackMPT::get_sync_samples); } AudioStreamPlaybackMPT::AudioStreamPlaybackMPT() {} @@ -423,6 +436,38 @@ bool AudioStreamMPT::_is_monophonic() const { return false; } +void AudioStreamMPT::set_skip_plugins(bool p_enable) { + this->skip_plugins = p_enable; +} + +bool AudioStreamMPT::get_skip_plugins() const { + return this->skip_plugins; +} + +void AudioStreamMPT::set_skip_subsongs_init(bool p_enable) { + this->skip_subsongs_init = p_enable; +} + +bool AudioStreamMPT::get_skip_subsongs_init() const { + return this->skip_subsongs_init; +} + +void AudioStreamMPT::set_sync_samples(bool p_enable) { + this->sync_samples = p_enable; +} + +bool AudioStreamMPT::get_sync_samples() const { + return this->sync_samples; +} + +std::map AudioStreamMPT::get_initial_ctls() const { + return std::map { + { "load.skip_plugins", this->skip_plugins ? "1" : "0" }, + { "load.skip_subsongs_init", this->skip_subsongs_init ? "1" : "0" }, + { "seek.sync_samples", this->sync_samples ? "1" : "0" }, + }; +} + void AudioStreamMPT::set_data(const PackedByteArray& p_data) { module_error = Error::OK; @@ -435,7 +480,7 @@ void AudioStreamMPT::set_data(const PackedByteArray& p_data) { if (this->mpt_module) delete mpt_module; try { - this->mpt_module = new openmpt::module(this->data.ptr(), this->data.size()); + this->mpt_module = new openmpt::module(this->data.ptr(), this->data.size(), std::clog, get_initial_ctls()); } catch (openmpt::exception& e) { module_error = Error::ERR_PARSE_ERROR; goto set_empty_module; @@ -445,7 +490,7 @@ void AudioStreamMPT::set_data(const PackedByteArray& p_data) { AudioServer::get_singleton()->lock(); for (AudioStreamPlaybackMPT* playback : open_playback_objects) { if (playback->mpt_module) delete playback->mpt_module; - playback->mpt_module = new openmpt::module_ext(this->data.ptr(), this->data.size()); + playback->mpt_module = new openmpt::module_ext(this->data.ptr(), this->data.size(), std::clog, get_initial_ctls()); playback->mpt_interactive = static_cast( playback->mpt_module->get_interface(openmpt::ext::interactive_id)); playback->mpt_interactive2 = static_cast( @@ -646,7 +691,7 @@ Ref AudioStreamMPT::_instantiate_playback() const { Ref playback; playback.instantiate(); playback->base = Ref(this); - playback->mpt_module = !data.is_empty() ? new openmpt::module_ext(this->data.ptr(), this->data.size()) : nullptr; + playback->mpt_module = !data.is_empty() ? new openmpt::module_ext(this->data.ptr(), this->data.size(), std::clog, get_initial_ctls()) : nullptr; if (playback->mpt_module) { playback->mpt_interactive = static_cast( @@ -671,6 +716,14 @@ void AudioStreamMPT::_bind_methods() { ClassDB::bind_method(D_METHOD("set_stereo", "stereo"), &AudioStreamMPT::set_stereo); ClassDB::bind_method(D_METHOD("is_stereo"), &AudioStreamMPT::is_stereo); + ClassDB::bind_method(D_METHOD("set_skip_plugins", "skip_plugins"), &AudioStreamMPT::set_skip_plugins); + ClassDB::bind_method(D_METHOD("get_skip_plugins"), &AudioStreamMPT::get_skip_plugins); + ClassDB::bind_method(D_METHOD("set_skip_subsongs_init", "skip_subsongs_init"), &AudioStreamMPT::set_skip_subsongs_init); + ClassDB::bind_method(D_METHOD("get_skip_subsongs_init"), &AudioStreamMPT::get_skip_subsongs_init); + + ClassDB::bind_method(D_METHOD("set_sync_samples", "sync_samples"), &AudioStreamMPT::set_sync_samples); + ClassDB::bind_method(D_METHOD("get_sync_samples"), &AudioStreamMPT::get_sync_samples); + ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamMPT::set_data); ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamMPT::get_data); @@ -716,6 +769,10 @@ void AudioStreamMPT::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled"), "set_loop_mode", "get_loop_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stereo"), "set_stereo", "is_stereo"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "skip_plugins"), "set_skip_plugins", "get_skip_plugins"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "skip_subsongs_init"), "set_skip_subsongs_init", "get_skip_subsongs_init"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync_samples"), "set_sync_samples", "get_sync_samples"); + BIND_ENUM_CONSTANT(LOOP_DISABLED); BIND_ENUM_CONSTANT(LOOP_ENABLED); diff --git a/src/audio_stream_mpt.h b/src/audio_stream_mpt.h index f672fab..f6429aa 100644 --- a/src/audio_stream_mpt.h +++ b/src/audio_stream_mpt.h @@ -99,6 +99,9 @@ class AudioStreamPlaybackMPT : public AudioStreamPlayback { void set_note_finetune(int32_t channel, double finetune); double get_note_finetune(int32_t channel) const; + void set_sync_samples(bool p_enable); + bool get_sync_samples() const; + virtual int32_t _mix(AudioFrame *p_buffer, double p_rate_scale, int32_t p_frames) override; AudioStreamPlaybackMPT(); @@ -124,6 +127,9 @@ class AudioStreamMPT : public AudioStream { private: LoopMode loop_mode = LoopMode::LOOP_DISABLED; bool stereo = true; + bool skip_plugins = false; + bool skip_subsongs_init = false; + bool sync_samples = true; PackedByteArray data; // We need to create a module to parse any information about the file, @@ -134,6 +140,8 @@ class AudioStreamMPT : public AudioStream { Error module_error = Error::OK; + std::map get_initial_ctls() const; + friend class AudioStreamPlaybackMPT; protected: static void _bind_methods(); @@ -147,6 +155,18 @@ class AudioStreamMPT : public AudioStream { virtual double _get_length() const override; virtual bool _is_monophonic() const override; + // Load CTLs + // These only apply on the next module load. + // Whether that means loading new data into this stream, or instantiating a new playback. + void set_skip_plugins(bool p_enable); + bool get_skip_plugins() const; + void set_skip_subsongs_init(bool p_enable); + bool get_skip_subsongs_init() const; + + // Seek CTLs + void set_sync_samples(bool p_enable); + bool get_sync_samples() const; + void set_data(const PackedByteArray& p_data); const PackedByteArray& get_data() const;