Skip to content

Commit

Permalink
bindings: add a libmapper binding
Browse files Browse the repository at this point in the history
  • Loading branch information
jcelerier committed Nov 26, 2024
1 parent aeb569b commit 9fc7f93
Show file tree
Hide file tree
Showing 4 changed files with 305 additions and 0 deletions.
84 changes: 84 additions & 0 deletions cmake/avendish.libmapper.cmake
Original file line number Diff line number Diff line change
@@ -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"
)
14 changes: 14 additions & 0 deletions include/avnd/binding/libmapper/configure.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once
#include <avnd/wrappers/configure.hpp>
#include <avnd/helpers/log.hpp>

#include <utility>

namespace libmapper
{
struct config
{
using logger_type = avnd::basic_logger;
};

}
181 changes: 181 additions & 0 deletions include/avnd/binding/libmapper/libmapper.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
#pragma once

/* SPDX-License-Identifier: GPL-3.0-or-later */

#include <avnd/wrappers/avnd.hpp>
#include <avnd/wrappers/controls.hpp>
#include <avnd/wrappers/process_adapter.hpp>
#include <avnd/introspection/messages.hpp>
#include <avnd/common/export.hpp>
#include <avnd/helpers/callback.hpp>


#include <mapper/mapper_cpp.h>


#include <span>
#include <cmath>
#include <string>

namespace libmapper
{
template <typename T>
struct processor
{
T& t;
mapper::Device dev;


static constexpr const int input_controls = avnd::parameter_input_introspection<T>::size;
static constexpr const int output_controls = avnd::parameter_output_introspection<T>::size;

std::array<mapper::Signal, input_controls> input_signals;
std::array<mapper::Signal, output_controls> output_signals;

template <auto Idx, typename C>
static std::string input_name(avnd::field_reflection<Idx, C>)
{
if constexpr (requires { C::name(); })
return "/" + std::string(C::name());
else
return "/in." + std::to_string(Idx);
}

template <auto Idx, typename C>
static std::string output_name(avnd::field_reflection<Idx, C>)
{
if constexpr (requires { C::name(); })
return "/" + std::string(C::name());
else
return "/out." + std::to_string(Idx);
}

template <auto Idx, typename C>
void setup_input(avnd::field_reflection<Idx, C> refl)
{
using namespace std::literals;
{
const std::string param_name = input_name(refl);
if constexpr(avnd::float_parameter<C>)
{
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<Idx>(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<C>)
{
static auto callback = [] (mapper::Signal&& sig, int value, mapper::Time&& time) {
T& obj = *(T*)(void*)sig["object"];
boost::pfr::get<Idx>(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 <auto Idx, typename C>
void setup_output(avnd::field_reflection<Idx, C> refl)
{
using namespace std::literals;
{
const std::string param_name = output_name(refl);

if constexpr(avnd::float_parameter<C>)
{
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<C>)
{
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<T>)
{
avnd::parameter_input_introspection<T>::for_all([this](auto a)
{ setup_input(a); });
}

if constexpr (avnd::outputs_is_value<T>)
{
avnd::parameter_output_introspection<T>::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<T>)
{
int k = 0;
avnd::parameter_output_introspection<T>::for_all(
t.outputs,
[this, &k] (auto& c) {
mapper::Signal& sig = this->output_signals[k++];
sig.set_value(c.value);
});
}
}
};
}
26 changes: 26 additions & 0 deletions include/avnd/binding/libmapper/prototype.cpp.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */

#include <@AVND_MAIN_FILE@>
#include <avnd/binding/libmapper/configure.hpp>
#include <avnd/binding/libmapper/libmapper.hpp>

void run(auto& t)
{
if_possible(t());
}

int main(int argc, char** argv)
{
// Create the object
using type = decltype(avnd::configure<libmapper::config, @AVND_MAIN_CLASS@>())::type;

type t;
static libmapper::processor< type > instance{t};

while (true)
{
instance.poll();
run(t);
instance.commit();
}
}

0 comments on commit 9fc7f93

Please sign in to comment.