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