diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dfcf2b2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,156 @@ +*.o +*.class +*.a +*.la +*.lo +*.so +*.dylib +*.dll +*.exe +*.pyc +Makefile.in +Makefile +.deps +.libs +.DS_Store +ChangeLog +README +aclocal.m4 +autom4te.cache +compile +config.guess +config.log +config.status +config.sub +configure +depcomp +install-sh +libtool +ltmain.sh +missing +doc/doxygen-build.stamp +doc/html/ +doc/latex/ +doc/libmapper.doxyfile +examples/pwm_example +examples/py_tk_gui/pwm.py +examples/py_tk_gui/setup.py +jni/mapper/NativeLib.java +jni/*.h +jni/*.log +jni/libmapper.jar +libmapper.tar.gz +libmapper.pc +m4/libtool.m4 +m4/ltoptions.m4 +m4/ltsugar.m4 +m4/ltversion.m4 +m4/lt~obsolete.m4 +NEWS +src/config.h +src/config.h.in +src/stamp-h1 +swig/build/ +swig/installed_files.log +swig/mapper.py +swig/mapper_wrap.c +swig/setup.py +test/test +test/testcalibrate +test/testconvergent +test/testcpp +test/testcustomtransport +test/testexpression +test/testgraph +test/testinstance +test/testinterrupt +test/testlinear +test/testlocalmap +test/testmany +test/testmapfail +test/testmapinput +test/testmapprotocol +test/testmonitor +test/testnetwork +test/testparams +test/testparser +test/testprops +test/testrate +test/testreverse +test/testsignalhierarchy +test/testsignals +test/testspeed +test/testthread +test/testunmap +test/testvector +### VisualStudio ### +build +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..e927796 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,58 @@ +cmake_minimum_required(VERSION 3.19) + +# Replace with local paths before building +set(MAX_SDK_DIR "/source/max-sdk-base") +set(LIBLO_DIR "") +set(C74_SUPPORT_DIR "${MAX_SDK_DIR}/c74support") +set(LIBLO_BUILD_DIR "${LIBLO_DIR}/cmake/build/Debug") +set(LIBLO_INCLUDES "${LIBLO_DIR}/cmake/build;${LIBLO_DIR}") + +project(oscmulticast) + +set(Liblo_LIB ${LIBLO_BUILD_DIR}/liblo.lib) +mark_as_advanced(Liblo_LIB) + +set(THIS_FOLDER_NAME "oscmulticast") + +include(${MAX_SDK_DIR}/script/max-pretarget.cmake) + +message("Generating: ${THIS_FOLDER_NAME}") + +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/build") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/Debug") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/Release") + +add_definitions( + -D_WINSOCK_DEPRECATED_NO_WARNINGS + -DHAVE_WINSOCK2_H + -DNODEFAULTLIB + -DMAXMSP +) + +############################################################# +# MAX EXTERNAL +############################################################# + +include_directories( + "${MAX_SDK_INCLUDES}" + "${MAX_SDK_MSP_INCLUDES}" + "${LIBLO_INCLUDES}" +) + +file(GLOB PROJECT_SRC + "${CMAKE_CURRENT_SOURCE_DIR}/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/*.c" + "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp" +) +add_library( + ${PROJECT_NAME} + MODULE + ${PROJECT_SRC} +) + +include(${MAX_SDK_DIR}/script/max-posttarget.cmake) + +target_link_libraries(${PROJECT_NAME} PUBLIC ${Liblo_LIB}) +target_link_libraries(${PROJECT_NAME} PUBLIC wsock32.lib ws2_32.lib iphlpapi.lib) + +set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "/NODEFAULTLIB:MSVCRTD") \ No newline at end of file diff --git a/README.markdown b/README.markdown new file mode 100644 index 0000000..61f3680 --- /dev/null +++ b/README.markdown @@ -0,0 +1,59 @@ +# oscmulticast external for MaxMSP and Pure Data + +An external object for sending and receiving Open Sound Control over multicast. Help patches are included for documentation. + +Windows support for the Pure Data external is not yet complete. + +On OSX you can build the externals using the XCode project. Windows users should use the instructions below. + +## Building the oscmulticast on Windows + +### Dependencies + +First, you'll need to clone the [max sdk][max-sdk]. + +[max-sdk]: https://github.com/Cycling74/max-sdk + +Cmake is also required to generate visual studio solutions, and can be installed [here][cmake]. Add it to the environment path when prompted for terminal access later on. + +[cmake]: https://cmake.org/download/ + +oscmulticast depends on version 0.30 of liblo or later. +Please clone the [LibLo repository][liblo] and consult its documentation to build for Windows. + +[liblo]: https://github.com/radarsat1/liblo + +Finally, you'll need Visual Studio 2017 or 2019, which you can grab [here][visual_studio]. Be sure to install the C++ developer tools when installing if you don't already have them. + +[visual_studio]: https://visualstudio.microsoft.com/vs/ + +### Configuring + +Now open a terminal in this project's root folder. + +Create a build directory and cd into it + + mkdir ./build + cd build + +Modify the CMakeLists.txt file in the root folder, replacing the paths near the top with your local paths. + +Comment out line 4 in /source/max-sdk-base/max-pretarget.cmake: + + #string(REGEX REPLACE "(.*)/" "" THIS_FOLDER_NAME "${CMAKE_CURRENT_SOURCE_DIR}") + +Commenting this line out corrects the external's name when exported. + +Run the following to generate a solution, replacing your version's details: + + cmake -G "Visual Studio 16 2019" .. + +### Building + +By now, you should have a Visual Studio solution in the ./build directory. Open the .sln and build the external project(s) of your choosing. + +Once built, copy the Max help files for each external to the output directory so that they're visible in Max. + +### Running + +Open Max and add the folder your external is built in with the `Options -> File Preferences` interface. Now you should be able to create your externals in a patcher. diff --git a/oscmulticast.c b/oscmulticast.c index c401350..e32aade 100755 --- a/oscmulticast.c +++ b/oscmulticast.c @@ -9,6 +9,10 @@ // ********************************************************* // -(Includes)---------------------------------------------- +#ifdef WIN32 +#define _WINSOCKAPI_ //for winsock1/2 conflicts +#endif + #ifdef MAXMSP #include "ext.h" // standard Max include, always required #include "ext_obex.h" // required for new style Max object @@ -22,9 +26,17 @@ #include #include #include +#ifndef WIN32 #include #include #include +#else +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#endif #include "lo/lo.h" #define INTERVAL 1 @@ -76,6 +88,13 @@ static void maxpd_atom_set_float(t_atom *a, float d); // -(global class pointer variable)------------------------- static void *oscmulticast_class; +#ifdef WIN32 +void ext_main(void* r) +{ + main(); +} +#endif + // ********************************************************* // -(main)-------------------------------------------------- #ifdef MAXMSP @@ -124,14 +143,83 @@ static int get_interface_addr(const char* pref, struct in_addr* addr, *(unsigned int *)&zero = inet_addr("0.0.0.0"); +#ifdef WIN32 + IP_ADAPTER_ADDRESSES *pAddresses = NULL; + + ULONG outBufLen = 15000; + int interations = 0; + ULONG res; + // Get list of adapter addresses + int iter = 0; + do { + pAddresses = (IP_ADAPTER_ADDRESSES*)malloc(outBufLen); + + res = + GetAdaptersAddresses(AF_UNSPEC, AF_INET, NULL, pAddresses, &outBufLen); + + if (res == ERROR_BUFFER_OVERFLOW) { + free(pAddresses); + pAddresses = NULL; + } + else { + break; + } + iter++; + } while (res == ERROR_BUFFER_OVERFLOW && iter < 100); + + // Search for preferred address and loopback + LPSOCKADDR chosenAddr = 0, loopbackAddr = 0; + char* pIfName = NULL; + if (res == NO_ERROR) { + IP_ADAPTER_ADDRESSES* pCurAddress = pAddresses; + while (pCurAddress != NULL) { + if (pCurAddress->OperStatus != IfOperStatusUp) { + pCurAddress = pCurAddress->Next; + continue; + } + IP_ADAPTER_MULTICAST_ADDRESS_XP* pCurMulticast = pCurAddress->FirstMulticastAddress; + while (pCurMulticast != NULL) { + if (pCurMulticast->Address.iSockaddrLength == 0) { + pCurMulticast = pCurMulticast->Next; + continue; + } + chosenAddr = pCurMulticast->Address.lpSockaddr; + pIfName = pCurAddress->AdapterName; + if (pref && strcmp(pCurAddress->AdapterName, pref) == 0) + break; + else if (pCurAddress->IfType == IF_TYPE_SOFTWARE_LOOPBACK) + loopbackAddr = pCurMulticast->Address.lpSockaddr; + pCurMulticast = pCurMulticast->Next; + } + pCurAddress = pCurAddress->Next; + if (pIfName == NULL) continue; + if (pref && strcmp(pIfName, pref) == 0) + break; + } + // Default to loopback address in case user is working locally. + if (chosenAddr == 0 && loopbackAddr != 0) + chosenAddr = loopbackAddr; + + if (chosenAddr != 0) { + if (*iface) + free(*iface); + *iface = strdup(pIfName); + sa = (struct sockaddr_in*)chosenAddr; + *addr = sa->sin_addr; + return 0; + } + } + + if (pAddresses) free(pAddresses); +#else struct ifaddrs *ifaphead; struct ifaddrs *ifap; struct ifaddrs *iflo=0, *ifchosen=0; if (getifaddrs(&ifaphead) != 0) return 1; - ifap = ifaphead; + while (ifap) { sa = (struct sockaddr_in *) ifap->ifa_addr; if (!sa) { @@ -165,6 +253,7 @@ static int get_interface_addr(const char* pref, struct in_addr* addr, } freeifaddrs(ifaphead); +#endif return 2; }