33#include < algorithm>
44#include < filesystem>
55#include < godot_cpp/classes/engine.hpp>
6+ #include < godot_cpp/classes/os.hpp>
7+ #include < godot_cpp/classes/project_settings.hpp>
68#include < godot_cpp/core/class_db.hpp>
79#include < godot_cpp/core/error_macros.hpp>
810
@@ -42,8 +44,8 @@ int AudioStreamPD::get_mix_rate() const {
4244// ////////////
4345
4446void AudioStreamPlaybackPD::_bind_methods () {
45- ClassDB::bind_method (D_METHOD (" open_patch" , " path " ), &AudioStreamPlaybackPD::open_patch);
46- ClassDB::bind_method (D_METHOD (" close_patch" , " path " ), &AudioStreamPlaybackPD::close_patch);
47+ ClassDB::bind_method (D_METHOD (" open_patch" , " relative_path " ), &AudioStreamPlaybackPD::open_patch);
48+ ClassDB::bind_method (D_METHOD (" close_patch" , " relative_path " ), &AudioStreamPlaybackPD::close_patch);
4749 ClassDB::bind_method (D_METHOD (" close_patch_id" , " dollar_zero" ), &AudioStreamPlaybackPD::close_patch_id);
4850 ClassDB::bind_method (D_METHOD (" send_bang" , " dest" ), &AudioStreamPlaybackPD::send_bang);
4951 ClassDB::bind_method (D_METHOD (" send_float" , " dest" , " value" ), &AudioStreamPlaybackPD::send_float);
@@ -100,13 +102,49 @@ pd::List AudioStreamPlaybackPD::_pd_list_from(const Array &p_arr) {
100102 return list;
101103}
102104
105+ String AudioStreamPlaybackPD::get_absolute_patch_path (String p_relative_path) {
106+ if (OS::get_singleton ()->has_feature (" editor" )) {
107+ // Running from an editor binary.
108+ return ProjectSettings::get_singleton ()->globalize_path (" res://" )
109+ .path_join (p_relative_path)
110+ .simplify_path ();
111+ } else {
112+ // Running from an exported project.
113+ //
114+ // This is NOT identical to using `ProjectSettings.globalize_path()` with a `res://` path,
115+ // but is close enough in spirit.
116+ // On macOS, this would be `YourGameName.app/Contents/MacOS`
117+ // see https://apple.stackexchange.com/questions/228116/add-delete-modify-files-within-a-disk-image-dmg//403082
118+ // and https://github.com/godotengine/godot/issues/22950
119+ //
120+ // Note that you will have to MANUALLY copy the `pd` directory there.
121+ // Setting "filter to exporting non-resource files" will pack it
122+ // into .pck file that is not accessable by libpd
123+ return OS::get_singleton ()->get_executable_path ()
124+ .get_base_dir ()
125+ .path_join (p_relative_path)
126+ .simplify_path ();
127+ }
128+ }
129+
103130AudioStreamPlaybackPD::AudioStreamPlaybackPD () {
104131 active = false ;
105132 stream = nullptr ;
106133 receiver.set_signaller (this );
107134 pd.setReceiver (&receiver);
108135 pd.setMidiReceiver (&receiver);
109136
137+ std::string path_to_else_subset_abstractions = std_string_from (
138+ OS::get_singleton ()->has_feature (" editor" )
139+ ? ProjectSettings::get_singleton ()->globalize_path (" res://addons/godot-pd/else_subset" )
140+ : OS::get_singleton ()->get_executable_path ().get_base_dir ().path_join (" else_subset" )
141+ );
142+ ERR_FAIL_COND_MSG (
143+ !fs::is_directory (fs::path (path_to_else_subset_abstractions)),
144+ " Trying to add " + godot_string_from (path_to_else_subset_abstractions) + " to libpd search path, but it does not exist"
145+ );
146+ pd.addToSearchPath (path_to_else_subset_abstractions);
147+
110148 // setup external libs
111149 else_subset_setup ();
112150}
@@ -159,12 +197,13 @@ void AudioStreamPlaybackPD::_start(double p_from_pos) {
159197 begin_resample ();
160198}
161199
162- int AudioStreamPlaybackPD::open_patch (String p_path) {
163- fs::path path = fs::path (std_string_from (p_path)).lexically_normal ();
200+ int AudioStreamPlaybackPD::open_patch (String p_relative_path) {
201+ String abs_path = get_absolute_patch_path (p_relative_path);
202+ fs::path path = fs::path (std_string_from (abs_path)).lexically_normal ();
164203 auto filename = path.filename ().string ();
165204 auto dir = path.parent_path ().string ();
166205
167- ERR_FAIL_COND_V_MSG (filename.empty (), -1 , " No filename included in path" + p_path );
206+ ERR_FAIL_COND_V_MSG (filename.empty (), -1 , " No filename included in path" + abs_path );
168207
169208 if (dir.empty ()) {
170209 dir = " ." ;
@@ -178,12 +217,13 @@ int AudioStreamPlaybackPD::open_patch(String p_path) {
178217 return patch.dollarZero ();
179218}
180219
181- void AudioStreamPlaybackPD::close_patch (String p_path) {
182- fs::path path = fs::path (std_string_from (p_path)).lexically_normal ();
220+ void AudioStreamPlaybackPD::close_patch (String p_relative_path) {
221+ String abs_path = get_absolute_patch_path (p_relative_path);
222+ fs::path path = fs::path (std_string_from (abs_path)).lexically_normal ();
183223 auto filename = path.filename ().string ();
184224 auto dir = path.parent_path ().string ();
185225
186- ERR_FAIL_COND_MSG (filename.empty (), " No filename included in path" + p_path );
226+ ERR_FAIL_COND_MSG (filename.empty (), " No filename included in path" + abs_path );
187227
188228 if (dir.empty ()) {
189229 dir = " ." ;
0 commit comments