diff --git a/.gitignore b/.gitignore index c95efac5ec16b1a6a4d48c1f89fd10e941455584..fa09e8c011c624117d34700cc93ffcf9ea70353d 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ *.import demo/build demo/export_presets.cfg +demo/android +build diff --git a/.gitmodules b/.gitmodules index aee1cced30f16eb4acb57ad58313c4765ef703b2..6c592ef7f6021bea930ccee10cd1b1abe256e65f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,3 +6,6 @@ path = src/godot-cpp url = https://github.com/GodotNativeTools/godot-cpp branch = 4.2 +[submodule "src/liblo"] + path = src/liblo + url = https://github.com/radarsat1/liblo.git diff --git a/SConstruct b/SConstruct index dff6e8f5fbcc598447ec883d4efc5e2b12a2c5cc..ba310745cb14a1ab5b333f7837c5e32bbe1fe8dd 100644 --- a/SConstruct +++ b/SConstruct @@ -19,6 +19,21 @@ fiddle_file = open("src/libpd/pure-data/extra/fiddle~/fiddle~.c","w") fiddle_file.writelines(out_lines) fiddle_file.close() +#Generate liblo headers +import fileinput + +#threadinclude = '#include "lo/lo_serverthread.h"' +threadinclude = '' +with open("src/liblo/lo/lo.h.in", "r") as file: + filedata = file.read() + filedata = filedata.replace("@THREADS_INCLUDE@", threadinclude) +with open("src/liblo/lo/lo.h", "w") as file: + file.write(filedata) +with open("src/liblo/lo/lo_endian.h.in", "r") as file: + filedata = file.read() + filedata = filedata.replace("@LO_BIGENDIAN@", "0") +with open("src/liblo/lo/lo_endian.h", "w") as file: + file.write(filedata) env = SConscript("src/godot-cpp/SConstruct") @@ -33,6 +48,7 @@ env = SConscript("src/godot-cpp/SConstruct") env.Append(CPPPATH=['.', 'src/libpd/libpd_wrapper', 'src/libpd/libpd_wrapper/util', + "src/liblo", 'src/libpd/cpp','src/libpd/pure-data/src', 'src/rtaudio'], CCFLAGS=["-fexceptions"]) @@ -85,7 +101,15 @@ sources = Glob('src/*.cpp') + Glob('src/rtaudio/*.cpp') +\ Glob('src/libpd/pure-data/src/s_net.c') +\ Glob('src/libpd/pure-data/src/s_path.c') +\ Glob('src/libpd/pure-data/src/s_print.c') +\ - Glob('src/libpd/pure-data/src/s_utf8.c') + Glob('src/libpd/pure-data/src/s_utf8.c') +\ + Glob("src/liblo/src/*.c",\ + exclude=["src/liblo/src/*test*",\ + "src/liblo/src/*.cpp",\ + "src/liblo/src/*thread*"]) + +env.Append(CPPDEFINES=[ 'LO_SO_VERSION={11,1,4}', + 'PACKAGE_VERSION=\\"0.31\\"', 'PACKAGE_NAME=\\"liblo\\"', + 'PRINTF_LL=""']) if env["platform"] == "linux": env.Append(CPPDEFINES=['__UNIX_JACK__', 'HAVE_LIBDL']) diff --git a/demo/Main.tscn b/demo/Main.tscn index dd3ecf60f4e091218b2aaa2d0d9e81067694af8c..83f3d8ac51beb03629177421266daaba5164646c 100644 --- a/demo/Main.tscn +++ b/demo/Main.tscn @@ -7,32 +7,41 @@ script/source = "extends Control func _ready(): add_child(_gdpd) - 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 _load_patch(pd_patch : String) : + + var fullpath = \"\" if OS.has_feature(\"editor\"): fullpath = ProjectSettings.globalize_path(pd_patch) else: - fullpath = OS.get_executable_path().get_base_dir().path_join(pd_patch.lstrip(\"res://\")) - #fullpath = pd_patch - #print(DirAccess.get_files_at(\"res://\")) + 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) + - print(fullpath) + print(\"fullpath = \", fullpath) # separate file name from directory var patch_name = fullpath.split(\"/\")[-1] var patch_dir = fullpath.trim_suffix(patch_name) # load patch - _gdpd.openfile(patch_name, \"./\") + _gdpd.openfile(patch_name, patch_dir) func _on_Start_pressed() : # retrieve the list of available input and outputs @@ -43,8 +52,8 @@ func _on_Start_pressed() : _gdpd.init_devices(inps[0], outs[0]) # the patch path should be absolute - _load_patch(\"res://patch1.pd\") - _load_patch(\"res://patch2.pd\") + _load_patch(\"patches/patch1.pd\") + _load_patch(\"patches/patch2.pd\") # send message to [receive from_godot] with one symbol _gdpd.start_message(1) @@ -60,8 +69,8 @@ func _on_start_non_rt_pressed(): _gdpd.init_nort() # the patch path should be absolute - _load_patch(\"res://patch1.pd\") - _load_patch(\"res://patch2.pd\") + _load_patch(\"patches/patch1.pd\") + _load_patch(\"patches/patch2.pd\") # send message to [receive from_godot] with one symbol _gdpd.start_message(1) @@ -72,8 +81,8 @@ func _on_start_non_rt_pressed(): _gdpd.subscribe(\"to_godot\") func _on_Stop_pressed(): - _gdpd.closefile(\"patch1.pd\") - _gdpd.closefile(\"patch2.pd\") + _gdpd.closefile(\"patches/patch1.pd\") + _gdpd.closefile(\"patches/patch2.pd\") _gdpd.stop() func _on_HSlider_value_changed(value): diff --git a/demo/patch1.pd b/demo/patches/patch1.pd similarity index 55% rename from demo/patch1.pd rename to demo/patches/patch1.pd index 82d8b965ff63eba8b115f18be8216809c79ee721..bee8fcfdb3037ab223c4eb996dfd8858c0f2d870 100644 Binary files a/demo/patch1.pd and b/demo/patches/patch1.pd differ diff --git a/demo/patch2.pd b/demo/patches/patch2.pd similarity index 100% rename from demo/patch2.pd rename to demo/patches/patch2.pd diff --git a/patches/gdpd.pd b/patches/gdpd.pd new file mode 100644 index 0000000000000000000000000000000000000000..952c2b7a6e0b70d2284d1c748246add79a565407 Binary files /dev/null and b/patches/gdpd.pd differ diff --git a/patches/gdpd_receive.pd b/patches/gdpd_receive.pd new file mode 100644 index 0000000000000000000000000000000000000000..d5fa2d14c1273045d6dd60cd2a1e9e5a1a37b48f Binary files /dev/null and b/patches/gdpd_receive.pd differ diff --git a/patches/gdpd_send.pd b/patches/gdpd_send.pd new file mode 100644 index 0000000000000000000000000000000000000000..61e18c7c8ea264bcef5e1d3525271bb6a4d40f0f Binary files /dev/null and b/patches/gdpd_send.pd differ diff --git a/src/gdpd.cpp b/src/gdpd.cpp index e5dd8aa6d4cc62b9b7c0edecfda8fa084d0cbfa6..fd6f6fabee20b6fbda511abb5864fe080f84df2b 100644 --- a/src/gdpd.cpp +++ b/src/gdpd.cpp @@ -98,7 +98,7 @@ Array GdPd::get_available_output_devices() { } -int GdPd::init_devices(String inputDevice, String outputDevice) { +void GdPd::init_devices(String inputDevice, String outputDevice) { std::string inpStr(inputDevice.utf8().get_data()); std::string outStr(outputDevice.utf8().get_data()); @@ -120,11 +120,9 @@ int GdPd::init_devices(String inputDevice, String outputDevice) { m_bufferFrames = 128; m_realtime=true; - - return start(); } -int GdPd::init_parameters(int nbInputs, int nbOutputs, int sampleRate, int bufferSize) { +void GdPd::init_parameters(int nbInputs, int nbOutputs, int sampleRate, int bufferSize) { m_inputDevice = m_audio.getDefaultInputDevice(); m_outputDevice = m_audio.getDefaultOutputDevice(); RtAudio::DeviceInfo inpInfo = m_audio.getDeviceInfo(m_inputDevice); @@ -135,12 +133,9 @@ int GdPd::init_parameters(int nbInputs, int nbOutputs, int sampleRate, int buffe m_bufferFrames = std::max<int>(64, bufferSize); m_realtime=true; - - return start(); } int GdPd::init_nort() { - m_nbInputs=0; m_nbOutputs=2; m_sampleRate=44100; @@ -173,56 +168,79 @@ int GdPd::init_nort() { } int GdPd::start() { - 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)); - - - 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(m_realtime) { - //intialize rtaudio - if(m_audio.getDeviceCount()==0){ - UtilityFunctions::print("There are no available sound devices."); + if(m_mode==OSC) { + + //Create OSC sender + m_sender = NULL; + m_sender = lo_server_new_multicast( + MULTICAST_ADDRESS.c_str(), SENDER_PORT, NULL); + m_receiver = NULL; + m_receiver = lo_server_new_multicast( + MULTICAST_ADDRESS.c_str(), RECEIVER_PORT, NULL); + /* + m_sender = lo_server_new_with_proto( + to_string(UDP_SERVER_PORT).c_str(), + LO_UDP, NULL); + */ + if(m_receiver!=NULL) { + lo_server_add_method(m_receiver, NULL, NULL, oscHandler, this); + } + else { + cout<<"GdPd : Could not create UDP connection"<<endl; + } + } + else { + 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)); + + if(!m_pd.init(m_nbInputs, m_nbOutputs, m_sampleRate, true)) { + print("GDPD : Error starting libpd"); return 1; } - RtAudio::StreamOptions options; - options.streamName = "gdpd"; - options.flags = RTAUDIO_SCHEDULE_REALTIME; - if(m_audio.getCurrentApi() != RtAudio::MACOSX_CORE) { - options.flags |= RTAUDIO_MINIMIZE_LATENCY; + //libpd_set_verbose(1); + + //start dsp + m_pd.computeAudio(true); + + if(m_mode==AUDIO_RT) { + //intialize rtaudio + if(m_audio.getDeviceCount()==0){ + UtilityFunctions::print("There are no available sound devices."); + return 1; + } + + RtAudio::StreamOptions options; + options.streamName = "gdpd"; + options.flags = RTAUDIO_SCHEDULE_REALTIME; + if(m_audio.getCurrentApi() != RtAudio::MACOSX_CORE) { + options.flags |= RTAUDIO_MINIMIZE_LATENCY; + } + try { + m_audio.openStream(&outParams, &inpParams, RTAUDIO_FLOAT32, + m_sampleRate, &m_bufferFrames, &audioCallback, + this, &options); + m_audio.startStream(); + print("Stream started"); + } + catch(RtAudioError& e) { + UtilityFunctions::print(e.getMessage().c_str()); + } } - try { - m_audio.openStream(&outParams, &inpParams, RTAUDIO_FLOAT32, - m_sampleRate, &m_bufferFrames, &audioCallback, - this, &options); - m_audio.startStream(); - print("Stream started"); + else { } - catch(RtAudioError& e) { - UtilityFunctions::print(e.getMessage().c_str()); - } - } - else { - } - //create message hook - m_pd.subscribe("to_gdpd"); - m_pd.setReceiver(this); - m_init=true; + //create message hook + m_pd.subscribe("to_gdpd"); + m_pd.setReceiver(this); + m_init=true; + } print("Initialized"); @@ -241,6 +259,17 @@ void GdPd::stop() { print("Stopped"); } +void GdPd::process(float delta) { + /* + if(m_udpServer!=NULL) { + lo_server_recv_noblock(m_udpServer, 10); + }*/ +} + +void GdPd::handleOSC(const char* path, const char* types, + lo_arg** argv, int argc, lo_message msg) { +} + void GdPd::processAudio(void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames) { int ticks = nBufferFrames / libpd_blocksize(); diff --git a/src/gdpd.h b/src/gdpd.h index 90e8d590b86979cb732ee61c7c101b14fe9e4900..b092b32a6ed8a420eb5e86d0b4ec1b6c57241a0e 100644 --- a/src/gdpd.h +++ b/src/gdpd.h @@ -42,6 +42,8 @@ #include "PdBase.hpp" #include "RtAudio.h" +#include <lo/lo.h> + using namespace godot; class GdPdStreamPlayback; @@ -52,6 +54,18 @@ class GdPd : public Node, public pd::PdReceiver { protected: static void _bind_methods(); + static int oscHandler(const char *path, const char *types, + lo_arg ** argv, + int argc, lo_message msg, void *user_data) { + GdPd* gdpd = (GdPd*)user_data; + gdpd->handleOSC(path, types, argv, argc, msg); + return 0; + } + static void errorHandler(int num, const char *msg, const char *path){ + std::cout<<"GdPd Error : "<<msg<<" "<<path<<std::endl; + } + virtual void handleOSC(const char* path, const char* types, + lo_arg** argv, int argc, lo_message msg){}; private: bool m_realtime; @@ -75,6 +89,13 @@ private: enum Mode {OSC, AUDIO, AUDIO_RT}; int m_mode; + const int SENDER_PORT = 9211; + const int RECEIVER_PORT = 9212; + const std::string MULTICAST_ADDRESS = "239.210.211.212"; + lo_server m_sender; + lo_server m_receiver; + int m_senderPort; + int m_receiverPort; private: int start(); diff --git a/src/liblo b/src/liblo new file mode 160000 index 0000000000000000000000000000000000000000..a3dc0cb3b73d0cfdf31463f26df72342f3c26e2b --- /dev/null +++ b/src/liblo @@ -0,0 +1 @@ +Subproject commit a3dc0cb3b73d0cfdf31463f26df72342f3c26e2b