From f132836c3367f30836519a52006a47a828b32961 Mon Sep 17 00:00:00 2001
From: Florent Berthaut <florent.berthaut@univ-lille.fr>
Date: Mon, 10 Feb 2025 17:07:33 +0100
Subject: [PATCH] Fix copying files

---
 SConstruct                               |   4 +-
 demo/Main.tscn                           |  24 +-
 demo/addons/gdpd/patches/gdpd.pd         | Bin 0 -> 2135 bytes
 demo/addons/gdpd/patches/gdpd_receive.pd | Bin 0 -> 146 bytes
 demo/addons/gdpd/patches/gdpd_send.pd    | Bin 0 -> 146 bytes
 demo/patches/patch1.pd                   | Bin 908 -> 1296 bytes
 patches/gdpd.pd                          | Bin 2266 -> 2135 bytes
 src/gdpd.cpp                             | 415 ++++++++++++-----------
 src/gdpd.h                               |  28 +-
 9 files changed, 253 insertions(+), 218 deletions(-)
 create mode 100644 demo/addons/gdpd/patches/gdpd.pd
 create mode 100644 demo/addons/gdpd/patches/gdpd_receive.pd
 create mode 100644 demo/addons/gdpd/patches/gdpd_send.pd

diff --git a/SConstruct b/SConstruct
index 5326d1c..b3cc802 100644
--- a/SConstruct
+++ b/SConstruct
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 import os
 import sys
+import shutil
 
 
 # Update submodules
@@ -122,7 +123,7 @@ if env["platform"] == "linux":
 	env.Append(CXXFLAGS=['-std=c++17'])
 elif env["platform"] == "android":
 	env.Append(CPPDEFINES=['__UNSPECIFIED__', 'HAVE_LIBDL', 'ANDROID',
-                        'HAS_SOCKLEN_T'])
+                        'HAS_SOCKLEN_T', 'HAVE_POLL=1'])
 	env.Append(CFLAGS=['-Wno-int-to-pointer-cast', '-Wno-pointer-to-int-cast',
 						'-Wno-discarded-qualifiers',
 						'-fPIC', '-O3', '-ffast-math', '-funroll-loops', 
@@ -148,5 +149,6 @@ else:
 	)
 
 # Copy pd patches
+shutil.copytree("patches", "demo/addons/gdpd/patches", dirs_exist_ok=True)
 
 Default(library)
diff --git a/demo/Main.tscn b/demo/Main.tscn
index a9b4740..edf9279 100644
--- a/demo/Main.tscn
+++ b/demo/Main.tscn
@@ -9,37 +9,20 @@ func _ready():
 	pass
 
 func _process(delta):
-	# Get messages from the patch
-	#while $GdPd.has_message() :
-	#	var msg = _gdpd.get_next()
-	#	print(\"got message from pd \", msg)
 	pass
 
 func _on_Stop_pressed():
-	_gdpd.close_patch(\"patches/patch1.pd\")
-	#_gdpd.close_patch(\"patches/patch2.pd\")
 	_gdpd.stop()
 
 func _on_HSlider_value_changed(value):
 	_gdpd.send(\"patch1\", [\"pitch\", value])
-	# send message to [receive from_godot] with three elements
-	#_gdpd.start_message(3)
-	#_gdpd.add_symbol(\"patch1\")
-	#_gdpd.add_symbol(\"pitch\")
-	#_gdpd.add_float(value)
-	#_gdpd.finish_list(\"from_godot\")
 	pass
 
 func _on_HSlider2_value_changed(value):
-	#_gdpd.start_message(3)
-	#_gdpd.add_symbol(\"patch2\")
-	#_gdpd.add_symbol(\"pitch\")
-	#_gdpd.add_float(value)
-	#_gdpd.finish_list(\"from_godot\")
 	pass
 
 func _on_gd_pd_got_message(address: String, arguments: Array) -> void:
-	print(\"got message \", address, arguments)
+	print(\"got message \", address, \" \", arguments)
 
 func _on_start_osc_pressed() -> void:
 	_gdpd.set_mode(0)
@@ -47,14 +30,10 @@ func _on_start_osc_pressed() -> void:
 
 func _on_start_audio_no_rt_pressed() -> void:
 	_gdpd.set_mode(1)
-	_gdpd.open_patch(\"patches/patch1.pd\")
-	#_load_patch(\"patches/patch2.pd\")
 	_gdpd.start()
 
 func _on_start_audio_rt_pressed() -> void:
 	_gdpd.set_mode(2)
-	_gdpd.open_patch(\"patches/patch1.pd\")
-	#_load_patch(\"patches/patch2.pd\")
 	_gdpd.start()
 "
 
@@ -132,6 +111,7 @@ offset_bottom = 260.0
 text = "Send pitch to patch2"
 
 [node name="GdPd" type="GdPd" parent="."]
+pd_patch = "res://patches/patch1.pd"
 
 [connection signal="pressed" from="StartOSC" to="." method="_on_start_osc_pressed"]
 [connection signal="pressed" from="StartAudioNoRT" to="." method="_on_start_audio_no_rt_pressed"]
diff --git a/demo/addons/gdpd/patches/gdpd.pd b/demo/addons/gdpd/patches/gdpd.pd
new file mode 100644
index 0000000000000000000000000000000000000000..a0843bc7bb6e8f312ae3e9bb1a4e3cf9d447f2d2
GIT binary patch
literal 2135
zcmY%PQ%FwCD@!a^urxDJFf}z+urxJOFflVzFf_8}QjSo_Ps&m-G%-^!GBZ`kOD!o%
zO-{`$OI6S<RnSdBRbXVGkdvR7l9ZU2jw)ekppajjT##5)oQfi0Y-p;GlUZD%P*Rkc
ziz;Jcpiq=wT9T@eo>GtkmohUpRxq+O!L-KM(m=t~T%jN(IX|zYC_e|RBeytR0VH8;
ztdLlgUaVjM76thSBx+#*u{$R<FTErKMas|!(*_fBGX+CaONA1JBoKtFH!?9%Fto5x
zD6Y&+%Fj`VQ89!|fow4}Fjgo|%}GrzQ7}+2R6qo&k*TGEv56@*eW1`bF)~xgFD=PQ
zEkPJ(u3%(os8FPkR+OI`pPrwRkB|dt0a;g~kf@Ld*I{H}qyRFg7^($1D9sJb6ikd1
ziWExn<59xc#L`m1$iM*IVW7YS`PInK5FBo)c_6<kSQ;5ZJPeWtYc(=fNY2m81Nk^c
z#So+b;b{wFBL$G7iov$ReE`yHYGS5fY6gyOkil?2n1I69*bpP!LF$c-&|L|NT#$?*
zC@q3gRzXo}L26!#LNQW6g1l~SqJSD!AYB$9YmfsF9IR%h(4<(9SdxrLDCWi%3WkQz
zL<F-26p{)Ebs&$Km?{_<LzJcFLDL{e+}y}i!N|f=AtkdI8eVWeo0*v_7#V^yO<I0Y
zZej@}SP<rdy=iWtU~FNgP?DabU}T_RppcqdP*RCP8Yt)*f@BPh6qM7FK~O>201V)?
zfdV4>K)y9Hfn>?lJW$LTDi|4C>KPdt=ouLr>KQ>x1d!`NInmhA5+2oP>Oh(y(GF5<
zZeXHdY>H59Xk@Nupl6_mmO4y9J~A}~l`_SkI72OBK(Z#rCdj!08g2?8KN>-U7A9t>
zV4z@ZiY90T5=0X-RxnU7K@&4kFi<eEL=!XxX*NR>G*d88us{<tS1?dOS7!mr{^)MB
zR4`C5N7D?6FhfjHLj^;07a4*)35t7g%7OdbNWoCS&=}nWBLyP`BXo~~l!1H-Nd#~c
zz+Oc+0OVCe3<JzSmSK1T<Xs~SQILO)(7gt7KPU!K`~Xs=V2mEHMxeMd!Vm?;7RV#0
zCYXR6W`O1iBNI?4U?dBWe?f+$ngH^!F?u+He5HU*)KI|~!!~d*peF?*3k9%R)G!An
zHw8m<wICxEjL_4KF(}^AEi(ouZuGzbxfC-XjX^<XjGml8*##pejKQIRVJs+(W5f?g
KKmla7H5ULQHz`&C

literal 0
HcmV?d00001

diff --git a/demo/addons/gdpd/patches/gdpd_receive.pd b/demo/addons/gdpd/patches/gdpd_receive.pd
new file mode 100644
index 0000000000000000000000000000000000000000..d5fa2d14c1273045d6dd60cd2a1e9e5a1a37b48f
GIT binary patch
literal 146
zcmY%PQ%FwCD@!a^urxDJFf}z+urxJOFflVzFf_8}QjSo_Ps&m-G%{1LFi|K{NGr<E
zjZaT0NI{V@v@lV~FD=PQEkPAFuv93@FD*$`h*2>F%OvOL<)tQ<C>SUhC>SXipokeM
J7$_Km!~oa@C9ePg

literal 0
HcmV?d00001

diff --git a/demo/addons/gdpd/patches/gdpd_send.pd b/demo/addons/gdpd/patches/gdpd_send.pd
new file mode 100644
index 0000000000000000000000000000000000000000..61e18c7c8ea264bcef5e1d3525271bb6a4d40f0f
GIT binary patch
literal 146
zcmY%PQ%FwCD@!a^urxDJFf}z+urxJOFflVzFf_8}QjSo_Ps&m-w@@%KQ^?HANi9JZ
zGc-0)C{`%Rk55l2NI{mcG*HONEG|(fC`v6z%}Y^;Q85HdC+Fwor6!js7$_Jh7%3Q_
Mh#4suC>Vmo0O@WfLjV8(

literal 0
HcmV?d00001

diff --git a/demo/patches/patch1.pd b/demo/patches/patch1.pd
index f1d010c8f321d83c238049dde8fe6d065bccbc77..7df27b00fd91b2d0dd544afe94a08aa442cc4ef7 100644
GIT binary patch
literal 1296
zcmY%PQ%FwCD@!a^urxDJFf}z+urxJOFflVzFf_8}QjSnaNlng4EJ{_-El4cMP|(xU
zPfSV4&nwnXPbo;z$F4$8Pro3sBsn9s7@{CQDNDh`+*rZT#8{zNp&$jU3nXY{V6I?n
zWT=pmm|TYtG*>XURLIFsOi4=2OGg$qG%!}kO)V+PS1>d%FhIx{m?;<;nJVO#<fkDC
znkyI@8z>azmzJa|6l9hpXMp8OQY%Un3{4bF4HSw~^HLOYQ;Ule(^C~n@)gqaQ}Rm?
zii`|Q6-+D?ic*tPGs{xp3e$@6bCHxnykVwbWMH6>T2WA>pps^&ZDI;G1}<l6sE}Wr
zT&G}Yh;X%;p*hGAgo77k3P`cJ0cKoa3u%yAP)LCy2+0}XPz8y_!+ZpeBSVA|a|J_F
zO9ikJaL^#h=N6|c7?~>=8d)f0q~_%0E5db|fm4dHLP>g#f{}rOfkJ9-K}jVFX`rBM
z2$C^0QczAy20;a712BNo1`0^-FtAiGG&NPoD9%wZG&g{l3N`?n;krhonGOyeQ$vM<
zqRc!<WP`#NlsYZUspJVzV1W!YG&EC4DK1cmQ9<^cxq^{}v4U2ef`J}F*wnyW!NdqP
z(SwWuiJO^211C8@FE2H@M8QbGK*3l6l9FIz#tH@shNftOCJF`$mS|$8Acf{=f))w}
z3MOb`mI?+6MuupDh9J8QEigq56%5VLML}*tcM`~rpzMe0dP8#s0|g9I!7eaBGZmc1
gFa!-jzCu?C5rhOXES!u$9xy@=J&*u6omq1M0Q_N9U;qFB

delta 357
zcmbQh)x$oa*2=`(Si#arAtygEB`Gm4-I_}|LLs*}T>&I+XlSO8Qe2>52o}vx%2F^g
zFjp`(GE_)OOs<=p#;h=LlCh+@f{~G_LT*WZ8j3PQvx)aI7|kcUGxoArC|FufzRBn-
z2r|RW&_cn`$V8!_C^N5QvLcgoeR6(YUTSiQf}w(ef{}s&*ch0Yk%9pTqKKI&7$_K{
zse_0?Tnf``s$igChNjL;!9c;#5=GFcM8O;+if*+9NYD^PX^Da%$dTrg6Ietio3IGV
QgN(C4S7Hg$Xbutu09znk0{{R3

diff --git a/patches/gdpd.pd b/patches/gdpd.pd
index 8d7aefbadbc8185576f6f091f5a2c131fae9377b..a0843bc7bb6e8f312ae3e9bb1a4e3cf9d447f2d2 100644
GIT binary patch
delta 171
zcmca5cwJz_PUguHEFbtx%`Fs6j7=2sOG|Q6OD3zZsxg{Q_GS%aG}}CvHHVSWVlx-J
z5u=8Mf`K)ca)d&1eqLT`a*2Ymv4VkuF@~tg<bDoOZ&L*W5Jc5rhE;<(R#6L(s0qkQ
E0Jg#`W&i*H

delta 228
zcmcaEa7%E*PG&<B0}BNsGYf@`;v5A-a{~n<0|f&GLnCtq5S^M^P*SOYOzIj<zRRpN
zc`Nf5b`t|51!F_Q$+MZIChuWUWHg(6k0p@Ne6s;-4kM%G=DBP}jEt6(FR+V>St=M<
zb16qCB<JVlr6!js7@H^<D40wZ;1G>9K$ikpY>c77Lcu@*Q`8b9YKmrp3CQ6PwE&*X
BJmmlY

diff --git a/src/gdpd.cpp b/src/gdpd.cpp
index 4a470d8..2d09a81 100644
--- a/src/gdpd.cpp
+++ b/src/gdpd.cpp
@@ -25,6 +25,8 @@
 
 #include <godot_cpp/core/class_db.hpp>
 #include <godot_cpp/classes/os.hpp>
+#include <godot_cpp/classes/project_settings.hpp>
+#include <godot_cpp/classes/dir_access.hpp>
 #include <godot_cpp/classes/global_constants.hpp>
 #include <godot_cpp/classes/label.hpp>
 #include <godot_cpp/variant/utility_functions.hpp>
@@ -37,16 +39,6 @@ const int PCM_BUFFER_SIZE = 4096 * 4;
 void GdPd::_bind_methods() {
 	ADD_GROUP("GdPd", "gdpd_");
 
-    /*
-    ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_RANGE, "0,2"), 
-                    "set_mode", "get_mode");
-*/
-    /*
-    ClassDB::add_property(get_class_static(), PropertyInfo(Variant::INT, "mode", 
-                PROPERTY_HINT_ENUM, "OSC, AUDIO, AUDIO_RT", 
-                PROPERTY_USAGE_DEFAULT), "set_mode", "get_mode");
-                */
-
 	ClassDB::bind_method(D_METHOD("get_available_input_devices"),
 					&GdPd::get_available_input_devices);
     ClassDB::bind_method(D_METHOD("get_available_output_devices"),
@@ -56,29 +48,33 @@ void GdPd::_bind_methods() {
     ClassDB::bind_method(D_METHOD("set_rt_defaults"), &GdPd::set_rt_defaults);
     ClassDB::bind_method(D_METHOD("start"), &GdPd::start);
     ClassDB::bind_method(D_METHOD("stop"), &GdPd::stop);
-    ClassDB::bind_method(D_METHOD("open_patch"), &GdPd::open_patch);
     ClassDB::bind_method(D_METHOD("close_patch"), &GdPd::close_patch);
 
     ClassDB::bind_method(D_METHOD("set_mode", "p_mode"), &GdPd::set_mode);
     ClassDB::bind_method(D_METHOD("get_mode"), &GdPd::get_mode);
 
+    ClassDB::bind_method(D_METHOD("set_osc_address", "p_osc_address"), 
+                         &GdPd::set_osc_address);
+    ClassDB::bind_method(D_METHOD("get_osc_address"), 
+                         &GdPd::get_osc_address);
+
+    ClassDB::bind_method(D_METHOD("set_pd_patch", "p_pd_patch"), 
+                         &GdPd::set_pd_patch);
+    ClassDB::bind_method(D_METHOD("get_pd_patch"), 
+                         &GdPd::get_pd_patch);
+
+
     ClassDB::bind_method(D_METHOD("send"), &GdPd::send);
 
-    /*
-    ClassDB::bind_method(D_METHOD("subscribe"), &GdPd::subscribe);
-    ClassDB::bind_method(D_METHOD("has_message"), &GdPd::has_message);
-    ClassDB::bind_method(D_METHOD("get_next"), &GdPd::get_next);
-    ClassDB::bind_method(D_METHOD("start_message"), &GdPd::start_message);
-    ClassDB::bind_method(D_METHOD("add_symbol"), &GdPd::add_symbol);
-    ClassDB::bind_method(D_METHOD("add_float"), &GdPd::add_float);
-    ClassDB::bind_method(D_METHOD("finish_list"), &GdPd::finish_list);
-    ClassDB::bind_method(D_METHOD("set_volume"), &GdPd::set_volume);
-    */
 
     ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", 
                     PROPERTY_HINT_ENUM, "OSC, AUDIO, AUDIO_RT", 
                     PROPERTY_USAGE_DEFAULT), 
                  "set_mode", "get_mode");
+    ADD_PROPERTY(PropertyInfo(Variant::STRING, "osc_address"), 
+                 "set_osc_address", "get_osc_address");
+    ADD_PROPERTY(PropertyInfo(Variant::STRING, "pd_patch", PROPERTY_HINT_FILE), 
+                 "set_pd_patch", "get_pd_patch");
 
 	ADD_SIGNAL(MethodInfo("got_message", 
                 PropertyInfo(Variant::STRING, "address"), 
@@ -88,23 +84,15 @@ void GdPd::_bind_methods() {
 GdPd::GdPd() {
     mode = 0;
     m_receiver = NULL;
-    //m_sender = NULL;
-	m_messages = new Array();
 	m_init=false;
+    m_rtDefaults=true;
+    m_player=NULL;
+    osc_address = String(MULTICAST_ADDRESS.c_str());
 }
 
 GdPd::~GdPd() {}
 
 
-void GdPd::set_mode(const int p_mode) {
-    std::cout<<"mode "<<p_mode<<std::endl;
-    mode = p_mode;
-}
-
-int GdPd::get_mode() const {
-    return mode;
-}
-
 int GdPd::audioCallback(void *outputBuffer, void *inputBuffer, 
 					    unsigned int nBufferFrames, double streamTime, 
 						RtAudioStreamStatus status, void *userData){
@@ -179,30 +167,6 @@ void GdPd::set_rt_defaults() {
 
 }
 
-/*
-int GdPd::start() {
-    m_nbInputs=0;
-    m_nbOutputs=2;
-    m_sampleRate=44100;
-    m_realtime=false;
-
-
-	if(!m_pd.init(m_nbInputs, m_nbOutputs, m_sampleRate, true)) {
-		print("GDPD : Error starting libpd");
-		return 1;
-	}
-
-	// Create message hook
-	m_pd.subscribe("to_gdpd");
-	m_pd.setReceiver(this);
-	m_init=true;
-
-
-	print("Initialized");
-
-	return 0;
-}*/
-
 int GdPd::start() {
     print("Starting with mode "+std::to_string(mode));
 
@@ -214,8 +178,10 @@ int GdPd::start() {
                             std::to_string(SENDER_PORT).c_str(), 
                             errorHandler);
                             */
+        //FIXME handle given ip address
         m_destination = lo_address_new(MULTICAST_ADDRESS.c_str(),
                                         std::to_string(SENDER_PORT).c_str());
+        //FIXME add local port in addition to multicast
         m_receiver = lo_server_new_multicast(
                             MULTICAST_ADDRESS.c_str(), 
                             std::to_string(RECEIVER_PORT).c_str(),
@@ -233,35 +199,54 @@ int GdPd::start() {
             print("Could not create UDP connection");
         }
     }
-    /*
     else { //AUDIO MODE
-        RtAudio::StreamParameters outParams, inpParams;
-        inpParams.deviceId = m_inputDevice;
-        inpParams.nChannels = m_nbInputs;
-        outParams.deviceId = m_outputDevice;
-        outParams.nChannels = m_nbOutputs;
-        print("Output channels = "+std::to_string(outParams.nChannels));
-        print("Input channels = "+std::to_string(inpParams.nChannels));
+        
+        // Retrieve defaults audio values if needed
+        if(m_rtDefaults) {
+            m_inputDevice = m_audio.getDefaultInputDevice();
+            m_outputDevice = m_audio.getDefaultOutputDevice();
+            RtAudio::DeviceInfo inpInfo = m_audio.getDeviceInfo(m_inputDevice);
+            RtAudio::DeviceInfo outInfo = m_audio.getDeviceInfo(m_outputDevice);
+            m_nbInputs 	= inpInfo.inputChannels;
+            m_nbOutputs = outInfo.outputChannels;
+            m_sampleRate = inpInfo.preferredSampleRate;
+            m_bufferFrames = 128;
+        }
 
+        if(mode==AUDIO) {
+            m_nbInputs=0; //FIXME Godot crashes when there are inputs
+            m_nbOutputs=2;
+            m_sampleRate=44100;
+        }
+
+        //libpd_set_verbose(1); //FIXME Add verbose control
         if(!m_pd.init(m_nbInputs, m_nbOutputs, m_sampleRate, true)) {
             print("GDPD : Error starting libpd");
             return 1;
         }
 
-        //libpd_set_verbose(1);
 
         //start dsp
         m_pd.computeAudio(true);
 
         if(mode==AUDIO_RT) {
+            print("Starting Audio RT");
             //intialize rtaudio
             if(m_audio.getDeviceCount()==0){
                 UtilityFunctions::print("There are no available sound devices.");
                 return 1;
             }
 
+            RtAudio::StreamParameters outParams, inpParams;
+            inpParams.deviceId = m_inputDevice;
+            inpParams.nChannels = m_nbInputs;
+            outParams.deviceId = m_outputDevice;
+            outParams.nChannels = m_nbOutputs;
+            print("Output channels = "+std::to_string(outParams.nChannels));
+            print("Input channels = "+std::to_string(inpParams.nChannels));
+
             RtAudio::StreamOptions options;
-            options.streamName = "gdpd";
+            options.streamName = "GdPd";
             options.flags = RTAUDIO_SCHEDULE_REALTIME;
             if(m_audio.getCurrentApi() != RtAudio::MACOSX_CORE) {
                 options.flags |= RTAUDIO_MINIMIZE_LATENCY;
@@ -278,26 +263,34 @@ int GdPd::start() {
             }
         }
         else {
-            // Create stream player
-            AudioStreamPlayer* p = memnew(AudioStreamPlayer());
-            add_child(p);
-
-            Ref<GdPdStream> s = memnew(GdPdStream());
-            s->setGdPd(this);
-
-            p->set_stream(s);
-            p->play();
+            print("Starting Audio non-RT");
+            // Create stream player if not already there
+            if(!m_player) {
+                m_player = memnew(AudioStreamPlayer());
+                add_child(m_player);
+                Ref<GdPdStream> s = memnew(GdPdStream());
+                s->setGdPd(this);
+                m_player->set_stream(s);
+            }
+            m_player->play();
         }
 
         //create message hook
         m_pd.subscribe("to_godot");
         m_pd.setReceiver(this);
+
+        // Open pd patch
+        if(!pd_patch.is_empty()) {
+            open_patch();
+        }
+        else {
+            print("Please select a pd patch");
+            return 1;
+        }
+
         m_init=true;
     }
 
-	print("Initialized");
-    */
-
 	return 0;
 }
 
@@ -308,28 +301,71 @@ void GdPd::stop() {
         lo_server_free(m_receiver);
         lo_address_free(m_destination);
     }
-    /*
     else {
         if(mode==AUDIO_RT) {
             m_audio.stopStream();
             m_audio.closeStream();
         }
         else {
+            if(m_player) {
+                m_player->stop();
+            }
+        }
+        if(m_patchOpened) {
+            m_pd.closePatch(pd_patch.utf8().get_data());
         }
         m_pd.computeAudio(false);
     }
-    */
 	print("Stopped");
 }
 
 void GdPd::send(String address, Array arguments) {
+    if(!m_init) {
+        return;
+    }
+
     if(mode==OSC) {
         lo_message msg = lo_message_new();
-        lo_message_add_int32(msg, 10);
+        for(int i=0; i<arguments.size(); ++i) {
+            switch(arguments[i].get_type()) {
+                case Variant::INT :
+                    lo_message_add_int32(msg, arguments[i]);
+                    break;
+                case Variant::FLOAT :
+                    lo_message_add_float(msg, arguments[i]);
+                    break;
+                case Variant::STRING :
+                    lo_message_add_string(msg, 
+                            String(arguments[i]).utf8().get_data());
+                    break;
+            }
+        }
+        
         std::string addrStr(address.utf8().get_data());
         addrStr = std::string("/gdpd/patch/")+addrStr;
         lo_send_message(m_destination, addrStr.c_str(), msg);
     }
+    else {
+        libpd_start_message(arguments.size()+2);
+        libpd_add_symbol("gdpd");
+        libpd_add_symbol("patch");
+        libpd_add_symbol(String(address).utf8().get_data());
+        for(int i=0; i<arguments.size(); ++i) {
+            switch(arguments[i].get_type()) {
+                case Variant::INT :
+                    libpd_add_float(arguments[i]);
+                    break;
+                case Variant::FLOAT :
+                    libpd_add_float(arguments[i]);
+                    break;
+                case Variant::STRING :
+                    std::string symbS(String(arguments[i]).utf8().get_data());
+                    libpd_add_symbol(symbS.c_str());
+                    break;
+            }
+        }
+        libpd_finish_list("from_godot");
+    }
 }
 
 void GdPd::_process(double delta) {
@@ -339,161 +375,161 @@ void GdPd::_process(double delta) {
 
     if(mode==OSC) {
         if(m_receiver!=NULL) {
-            int res = lo_server_recv_noblock(m_receiver, 1);
-            //print("received "+std::to_string(res));
+            lo_server_recv_noblock(m_receiver, 1);
         }
     }
-    /*
     else {
         m_pd.receiveMessages();
-
-        //FIXME for each message, call signal with address and arguments
-        emit_signal("got_message", "blup");
     }
-    */
 }
 
 void GdPd::handleOSC(const char* path, const char* types, 
                      lo_arg** argv, int argc, lo_message msg) {
-    print(path);
-    //FIXME call signal with address and arguments
-    emit_signal("got_message", "blup");
+	Array gdList;
+	for(int i = 0; i < argc; ++i) {
+        switch(types[i]) {
+            case 'f' :
+                gdList.push_back(argv[i]->f);
+                break;
+            case 'i' :
+                gdList.push_back(argv[i]->i);
+                break;
+            case 's' :
+                gdList.push_back(String(&(argv[i]->s)));
+                break;
+		}
+	}
+    emit_signal("got_message", String(path), gdList);
 }
 
 void GdPd::processAudio(void *outputBuffer, void *inputBuffer, 
 						unsigned int nBufferFrames) {
-    /*
 	int ticks = nBufferFrames / libpd_blocksize();
 
 	m_pd.processFloat(ticks, (float*)inputBuffer, (float*)outputBuffer);
-
-	//volume control on the output
-	for(int b=0; b<nBufferFrames*m_nbOutputs; ++b) {
-		((float*)outputBuffer)[b]*=m_vol;
-	}
-    */
 }
 
-bool GdPd::open_patch(godot::String patch) {
-    std::string patchStr(patch.utf8().get_data());
+bool GdPd::open_patch() {
+    std::string pdPatchStr;
+    std::string pdDirStr;
 
     /*
-    	var fullpath = ""
-	if OS.has_feature("editor"):
-		fullpath = ProjectSettings.globalize_path(pd_patch)
-	else:
-		var file = pd_patch.split("/")[-1]
-		var path = pd_patch.trim_suffix(file)
-		# Copy files from the patches folder from res to user
-		if OS.get_name() == "Android" :
-			fullpath = "/sdcard/Android/data/com.example.gdpd/files/"
-		else :
-			fullpath = OS.get_user_data_dir()
-		var dir = DirAccess.open("res://")
-		fullpath = fullpath.path_join(file)
-		# FIXME do that in C++, and for all .pd and .wav files of the project, on start
-		dir.copy("res://"+path+"/"+file, fullpath)
-        	var patch_name = fullpath.split("/")[-1]
-	var patch_dir = fullpath.trim_suffix(patch_name)
-*/
-    /*
-	std::string baseS(baseStr.utf8().get_data());
-	std::string dirS(dirStr.utf8().get_data());
+    if(OS::get_singleton()->has_feature("editor")) {
+        print("Loading patch from editor");
+		String fullpath = ProjectSettings::get_singleton()
+                            ->globalize_path(pd_patch);
 
-	if(m_patchsMap.find(baseS)!=m_patchsMap.end()) {
-		print("Patch "+baseS+" already opened");
-		return true;
-	}
+        
+
+		String pat = fullpath.get_slice("/",fullpath.get_slice_count("/")-1);
+		String dir = fullpath.trim_suffix(pat);
 
-	pd::Patch p1 = m_pd.openPatch(baseS.c_str(), dirS.c_str());
+        pdPatchStr = std::string(pat.utf8().get_data());
+        pdDirStr = std::string(dir.utf8().get_data());
+    }
+    else {*/
+        print("Loading patch from package");
+
+        String userPath; 
+
+		if(OS::get_singleton()->get_name() == "Android") {
+            print("On Android, so start copying files to user folder");
+            String packageName = "com.example.gdpd";
+
+            //FIXME get Android package name
+            userPath = String("/sdcard/Android/data/")
+                        + packageName
+                        + String("/files/");
+        }
+        else {
+			userPath = OS::get_singleton()->get_user_data_dir()+"/";
+        }
+
+
+        //Get the folder containing the main pd patch
+		String patch = pd_patch.get_slice("/",pd_patch.get_slice_count("/")-1);
+		String patchDir = pd_patch.trim_suffix(patch).trim_prefix("res://");
+
+        //Recursively copy this folder from res to user directory
+        recurseCopy("res://"+patchDir, userPath+patchDir);
+
+        //Also copy gdpd patches
+        std::vector<String> gdpdFiles{"gdpd.pd", "gdpd_send.pd", 
+                                      "gdpd_receive.pd"};
+        for(auto& f : gdpdFiles) {
+            DirAccess::copy_absolute("res://addons/gdpd/patches/"+f,
+                                     userPath+patchDir+"/"+f);
+        }
+
+        //Define new paths
+        pdPatchStr = std::string(patch.utf8().get_data());
+        pdDirStr = std::string((userPath+patchDir).utf8().get_data());
+    //}
+
+
+    // Open the patch
+    m_patchOpened=false;
+	pd::Patch p1 = m_pd.openPatch(pdPatchStr.c_str(), pdDirStr.c_str());
 	if(!p1.isValid()) {
-		print("Could not open patch "+baseS);
+		print("Could not open patch "+pdPatchStr);
         return false;
 	}
 	else {
-		print("Opened patch "+baseS);
-		m_patchsMap[baseS] = p1;
-	}*/
-    return true;
-}
-
-void GdPd::close_patch(godot::String patch) {
-	std::string patchStr(patch.utf8().get_data());
-    /*
-	if(m_patchsMap.find(patchStr)!=m_patchsMap.end()) {
-		m_pd.closePatch(m_patchsMap[patchStr]);
-		m_patchsMap.erase(patchStr);
-		print("Closed patch "+patchStr);
-	}*/
-}
-
-/*
-void GdPd::subscribe(String symbStr) {
-	std::string symbS(symbStr.utf8().get_data());
-	m_pd.subscribe(symbS.c_str());
-}*/
-
-/*
-bool GdPd::has_message() {
-	if(m_init) {
-		//receive new messages
-		m_pd.receiveMessages();
+		print("Opened patch "+pdPatchStr);
+        m_patchOpened=true;
 	}
-
-	//return if more than one message
-	int size = m_messages->size();
-    return size>0;
+    return true;
 }
 
-Array GdPd::get_next() {
-	Array msg = m_messages->pop_front();
-	return msg;
-}
+void GdPd::recurseCopy(String fromPath, String toPath) {
 
-int GdPd::blocksize() {
-	int blocksize = libpd_blocksize();
-	return blocksize;
-}
+    //Create the toPath folder
+    DirAccess::make_dir_absolute(toPath);
 
-int GdPd::start_message(int nbValues) {
-    int res = libpd_start_message(nbValues);
-	return res;
-}
+    //For each file inside the origin directory
+    PackedStringArray files = DirAccess::get_files_at(fromPath);
+    for(int i=0; i<files.size(); ++i) {
+        String fileName = files[i];
+        print("File "+std::string(fileName.utf8().get_data()));
+        DirAccess::copy_absolute(fromPath+"/"+fileName, 
+                                 toPath+"/"+fileName);
+    }
 
-void GdPd::add_symbol(String symbStr) {
-	std::string symbS(symbStr.utf8().get_data());
-	libpd_add_symbol(symbS.c_str());
-}
+    //For each dir, call recursCopy
+    PackedStringArray dirs = DirAccess::get_directories_at(fromPath);
+    for(int i=0; i<dirs.size(); ++i) {
+        String dirName = dirs[i];
+        print("Dir "+std::string(dirName.utf8().get_data()));
+    }
 
-void GdPd::add_float(float val) {
-	libpd_add_float(val);
 }
 
-int GdPd::finish_list(String destStr) {
-	std::string destS(destStr.utf8().get_data());
-    int res = libpd_finish_list(destS.c_str());
-    return res;
+void GdPd::close_patch() {
+    /*
+	std::string patchStr(patch.utf8().get_data());
+	if(m_patchsMap.find(patchStr)!=m_patchsMap.end()) {
+		m_pd.closePatch(m_patchsMap[patchStr]);
+		m_patchsMap.erase(patchStr);
+		print("Closed patch "+patchStr);
+	}*/
 }
-*/
 
 void GdPd::print(const std::string& message) {
 	UtilityFunctions::print((std::string("GdPd : ")+message).c_str());
 }
 
 void GdPd::receiveList(const std::string& dest, const pd::List& list) {
-	Array gdlist;
-
+	Array gdList;
 	for(int i = 0; i < list.len(); ++i) {
 		if(list.isFloat(i)) {
-			gdlist.push_back(list.getFloat(i));
+			gdList.push_back(list.getFloat(i));
 		}
 		else if(list.isSymbol(i)) {
 			String symbStr(list.getSymbol(i).c_str());
-			gdlist.push_back(symbStr);
+			gdList.push_back(symbStr);
 		}
 	}
-
-	m_messages->push_back(gdlist);
+    emit_signal("got_message", String(dest.c_str()), gdList);
 }
 
 /* GdPdStream */
@@ -503,7 +539,6 @@ GdPdStream::GdPdStream() {
 
 Ref<AudioStreamPlayback> GdPdStream::_instantiate_playback() const {
     Ref<GdPdStreamPlayback> gdPlayback = memnew(GdPdStreamPlayback());
-    //gdPlayback.instantiate();
     gdPlayback->m_base = Ref<GdPdStream>(this);
     return gdPlayback;
 }
@@ -512,7 +547,7 @@ Ref<AudioStreamPlayback> GdPdStream::_instantiate_playback() const {
 
 /* GdPdStreamplayback */
 GdPdStreamPlayback::GdPdStreamPlayback() {
-    m_buffer.resize(4096*2,0.0);
+    m_buffer.resize(PCM_BUFFER_SIZE,0.0);
     AudioServer::get_singleton()->lock();
 	pcm_buffer = memalloc(PCM_BUFFER_SIZE);
 	zeromem(pcm_buffer, PCM_BUFFER_SIZE);
@@ -525,7 +560,7 @@ GdPdStreamPlayback::~GdPdStreamPlayback() {
 
 int32_t GdPdStreamPlayback::_mix_resampled(AudioFrame *buffer, 
                                            int32_t nbFrames) {
-    m_buffer.resize(4096*2, 0.0);
+    m_buffer.resize(PCM_BUFFER_SIZE, 0.0);
     m_base->getGdPd()->processAudio(m_buffer.data(), NULL, nbFrames);
     for(int i = 0; i < nbFrames; i++) {
         buffer[i].left = m_buffer[i*2];
diff --git a/src/gdpd.h b/src/gdpd.h
index f41ff85..7f0c7a2 100644
--- a/src/gdpd.h
+++ b/src/gdpd.h
@@ -67,6 +67,7 @@ protected:
     }
     void handleOSC(const char* path, const char* types, 
                     lo_arg** argv, int argc, lo_message msg);
+    void recurseCopy(String fromPath, String toPath);
 
 private:
     bool m_realtime;
@@ -84,16 +85,23 @@ private:
 	int m_inputDevice;
 	int m_outputDevice;
 	std::map<std::string, pd::Patch> m_patchsMap;
+    bool m_rtDefaults;
+
+    AudioStreamPlayer* m_player;
 
 	bool m_init;
 
     enum Mode {OSC, AUDIO, AUDIO_RT};
     int mode;
 
+    String pd_patch;
+    String pd_folder;
+    bool m_patchOpened;
+
     const int SENDER_PORT = 9211;
     const int RECEIVER_PORT = 9212;
     const std::string MULTICAST_ADDRESS = "239.210.211.212";
-    //lo_server m_sender;
+    String osc_address;
     lo_server m_receiver;
     lo_address m_destination;
     int m_senderPort;
@@ -104,8 +112,8 @@ public:
 	GdPd();
 	virtual ~GdPd();
 
-    void set_mode(const int p_mode);
-    int get_mode() const;
+    inline void set_mode(const int p_mode){mode=p_mode;}
+    inline int get_mode() const {return mode;}
 
 	Array get_available_input_devices();
 	Array get_available_output_devices();
@@ -118,8 +126,18 @@ public:
 
     void _process(double delta) override;
 
-	bool open_patch(String basename);
-	void close_patch(String basename);
+	bool open_patch();
+	void close_patch();
+
+    inline void set_pd_folder(const String p_folder){pd_folder=p_folder;}
+    inline String get_pd_folder() const{return pd_folder;}
+    inline void set_pd_patch(const String p_patch){pd_patch=p_patch;}
+    inline String get_pd_patch() const{return pd_patch;}
+
+    inline void set_osc_address(const String p_osc_address) { 
+        osc_address = p_osc_address;
+    }
+    inline String get_osc_address() const{return osc_address;}
 
     void send(String destination, Array arguments);
 
-- 
GitLab