diff --git a/cmake/avendish.libmapper.cmake b/cmake/avendish.libmapper.cmake new file mode 100644 index 0000000..66ed86f --- /dev/null +++ b/cmake/avendish.libmapper.cmake @@ -0,0 +1,84 @@ +find_path(LIBMAPPER_INCLUDE_DIR + mapper/mapper.h + HINTS + /opt/libmapper/include +) + +find_library(LIBMAPPER_LIBRARIES mapper + HINTS + /opt/libmapper/lib +) + +if(LIBMAPPER_INCLUDE_DIR AND LIBMAPPER_LIBRARIES) + add_library(mapper INTERFACE) + target_include_directories(mapper INTERFACE "${LIBMAPPER_INCLUDE_DIR}") + target_link_libraries(mapper INTERFACE "${LIBMAPPER_LIBRARIES}") +endif() + +if(NOT LIBMAPPER_INCLUDE_DIR) + function(avnd_make_libmapper) + endfunction() + + return() +endif() + +function(avnd_make_libmapper) + cmake_parse_arguments(AVND "" "TARGET;MAIN_FILE;MAIN_CLASS;C_NAME" "" ${ARGN}) + + string(MAKE_C_IDENTIFIER "${AVND_MAIN_CLASS}" MAIN_OUT_FILE) + + configure_file( + "${AVND_SOURCE_DIR}/include/avnd/binding/libmapper/prototype.cpp.in" + "${CMAKE_BINARY_DIR}/${MAIN_OUT_FILE}_libmapper.cpp" + @ONLY + NEWLINE_STYLE LF + ) + + set(AVND_FX_TARGET "${AVND_TARGET}_libmapper") + add_executable(${AVND_FX_TARGET}) + + set_target_properties( + ${AVND_FX_TARGET} + PROPERTIES + OUTPUT_NAME "${AVND_C_NAME}" + LIBRARY_OUTPUT_DIRECTORY libmapper + RUNTIME_OUTPUT_DIRECTORY libmapper + ) + + target_sources( + ${AVND_FX_TARGET} + PRIVATE + "${AVND_MAIN_FILE}" + "${CMAKE_BINARY_DIR}/${MAIN_OUT_FILE}_libmapper.cpp" + ) + + target_include_directories( + ${AVND_FX_TARGET} + PRIVATE + "${LIBMAPPER_INCLUDE_DIR}" + ) + + target_compile_definitions( + ${AVND_FX_TARGET} + PRIVATE + AVND_LIBMAPPER=1 + ) + + target_link_libraries( + ${AVND_FX_TARGET} + PRIVATE + Avendish::Avendish_libmapper + mapper + DisableExceptions + ) + + avnd_common_setup("${AVND_TARGET}" "${AVND_FX_TARGET}") +endfunction() + +add_library(Avendish_libmapper INTERFACE) +target_link_libraries(Avendish_libmapper INTERFACE Avendish) +add_library(Avendish::Avendish_libmapper ALIAS Avendish_libmapper) + +target_sources(Avendish PRIVATE + "${AVND_SOURCE_DIR}/include/avnd/binding/libmapper/libmapper.hpp" +) diff --git a/include/avnd/binding/libmapper/configure.hpp b/include/avnd/binding/libmapper/configure.hpp new file mode 100644 index 0000000..d93b4ab --- /dev/null +++ b/include/avnd/binding/libmapper/configure.hpp @@ -0,0 +1,14 @@ +#pragma once +#include +#include + +#include + +namespace libmapper +{ +struct config +{ + using logger_type = avnd::basic_logger; +}; + +} diff --git a/include/avnd/binding/libmapper/libmapper.hpp b/include/avnd/binding/libmapper/libmapper.hpp new file mode 100644 index 0000000..dbf8b1f --- /dev/null +++ b/include/avnd/binding/libmapper/libmapper.hpp @@ -0,0 +1,181 @@ +#pragma once + +/* SPDX-License-Identifier: GPL-3.0-or-later */ + +#include +#include +#include +#include +#include +#include + + +#include + + +#include +#include +#include + +namespace libmapper +{ +template +struct processor +{ + T& t; + mapper::Device dev; + + + static constexpr const int input_controls = avnd::parameter_input_introspection::size; + static constexpr const int output_controls = avnd::parameter_output_introspection::size; + + std::array input_signals; + std::array output_signals; + + template + static std::string input_name(avnd::field_reflection) + { + if constexpr (requires { C::name(); }) + return "/" + std::string(C::name()); + else + return "/in." + std::to_string(Idx); + } + + template + static std::string output_name(avnd::field_reflection) + { + if constexpr (requires { C::name(); }) + return "/" + std::string(C::name()); + else + return "/out." + std::to_string(Idx); + } + + template + void setup_input(avnd::field_reflection refl) + { + using namespace std::literals; + { + const std::string param_name = input_name(refl); + if constexpr(avnd::float_parameter) + { + std::cerr << param_name << std::endl; + static auto callback = [] (mapper::Signal&& sig, float value, mapper::Time&& time) { + T& obj = *(T*)(void*)sig["object"]; + boost::pfr::get(obj.inputs).value = value; + }; + if constexpr(requires { C::control(); }) + { + static const auto ctl = C::control(); + input_signals[Idx] = dev.add_signal( + mapper::Direction::INCOMING, param_name, 1, + mapper::Type::FLOAT, "", (void*)&ctl.min, (void*)&ctl.max, nullptr) + .set_property("object", (void*)&t) + .set_callback(callback, mapper::Signal::Event::UPDATE); + } + else + { + static float min0 = 0.f; + static float max1 = 1.f; + input_signals[Idx] = dev.add_signal( + mapper::Direction::INCOMING, param_name, 1, + mapper::Type::FLOAT, "", (void*)&min0, (void*)&max1, nullptr) + .set_property("object", (void*)&t) + .set_callback(callback, mapper::Signal::Event::UPDATE); + } + } + else if constexpr(avnd::int_parameter) + { + static auto callback = [] (mapper::Signal&& sig, int value, mapper::Time&& time) { + T& obj = *(T*)(void*)sig["object"]; + boost::pfr::get(obj.inputs).value = value; + }; + if constexpr(requires { C::control(); }) + { + static const auto ctl = C::control(); + input_signals[Idx] = dev.add_signal( + mapper::Direction::INCOMING, param_name, 1, + mapper::Type::INT32, "", (void*)&ctl.min, (void*)&ctl.max, nullptr) + .set_property("object", (void*)&t) + .set_callback(callback, mapper::Signal::Event::UPDATE); + } + } + } + } + + template + void setup_output(avnd::field_reflection refl) + { + using namespace std::literals; + { + const std::string param_name = output_name(refl); + + if constexpr(avnd::float_parameter) + { + if constexpr(requires { C::control(); }) + { + static const auto ctl = C::control(); + output_signals[Idx] = dev.add_signal( + mapper::Direction::OUTGOING, param_name, 1, + mapper::Type::FLOAT, "", (void*)&ctl.min, (void*)&ctl.max, nullptr); + } + else + { + static float min0 = 0.f; + static float max1 = 1.f; + output_signals[Idx] = dev.add_signal( + mapper::Direction::OUTGOING, param_name, 1, + mapper::Type::FLOAT, "", (void*)&min0, (void*)&max1, nullptr); + } + } + else if constexpr(avnd::int_parameter) + { + if constexpr(requires { C::control(); }) + { + static const auto ctl = C::control(); + output_signals[Idx] = dev.add_signal( + mapper::Direction::OUTGOING, param_name, 1, + mapper::Type::INT32, "", (void*)&ctl.min, (void*)&ctl.max, nullptr); + } + } + } + } + + explicit processor(T& t) + : t{t} + , dev{T::name()} + { + if constexpr (avnd::inputs_is_value) + { + avnd::parameter_input_introspection::for_all([this](auto a) + { setup_input(a); }); + } + + if constexpr (avnd::outputs_is_value) + { + avnd::parameter_output_introspection::for_all([this](auto a) + { setup_output(a); }); + } + fflush(stdout); + fflush(stderr); + } + + void poll() + { + dev.poll(10); + } + + void commit() + { + if constexpr (avnd::outputs_is_value) + { + int k = 0; + avnd::parameter_output_introspection::for_all( + t.outputs, + [this, &k] (auto& c) { + mapper::Signal& sig = this->output_signals[k++]; + sig.set_value(c.value); + }); + } + } +}; +} diff --git a/include/avnd/binding/libmapper/prototype.cpp.in b/include/avnd/binding/libmapper/prototype.cpp.in new file mode 100644 index 0000000..56ec73b --- /dev/null +++ b/include/avnd/binding/libmapper/prototype.cpp.in @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ + +#include <@AVND_MAIN_FILE@> +#include +#include + +void run(auto& t) +{ + if_possible(t()); +} + +int main(int argc, char** argv) +{ + // Create the object + using type = decltype(avnd::configure())::type; + + type t; + static libmapper::processor< type > instance{t}; + + while (true) + { + instance.poll(); + run(t); + instance.commit(); + } +} \ No newline at end of file