diff --git a/cmake/avendish.cmake b/cmake/avendish.cmake index 3d4266f4..c02c7847 100644 --- a/cmake/avendish.cmake +++ b/cmake/avendish.cmake @@ -9,7 +9,7 @@ set(AVND_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" CACHE INTERNAL "") find_package(Boost QUIET REQUIRED) find_package(Threads QUIET) find_package(fmt QUIET) - +find_package(ossia QUIET) set(AVENDISH_SOURCES "${AVND_SOURCE_DIR}/include/avnd/concepts/all.hpp" diff --git a/cmake/avendish.dependencies.cmake b/cmake/avendish.dependencies.cmake index 61b4e8d3..56d4aaf9 100644 --- a/cmake/avendish.dependencies.cmake +++ b/cmake/avendish.dependencies.cmake @@ -41,3 +41,20 @@ if(NOT TARGET pantor::inja) ) FetchContent_MakeAvailable(pantor_inja) endif() + +if(APPLE) +if(NOT TARGET jthread) +FetchContent_Declare( + jthread + GIT_REPOSITORY "https://github.com/StirlingLabs/jthread" + GIT_TAG main + GIT_PROGRESS true +) +FetchContent_MakeAvailable(jthread) +endif() +endif() + + +if(NOT TARGET jthread) + add_library(jthread INTERFACE) +endif() diff --git a/cmake/avendish.ossia.cmake b/cmake/avendish.ossia.cmake index f88962b7..8a2c9663 100644 --- a/cmake/avendish.ossia.cmake +++ b/cmake/avendish.ossia.cmake @@ -67,9 +67,22 @@ function(avnd_make_ossia) PUBLIC Avendish::Avendish ossia::ossia - SDL2 ) + if(TARGET SDL2::SDL2) + target_link_libraries( + ${AVND_FX_TARGET} + PUBLIC + SDL2::SDL2 + ) + elseif(TARGET SDL2::SDL2-static) + target_link_libraries( + ${AVND_FX_TARGET} + PUBLIC + SDL2::SDL2-static + ) + endif() + avnd_common_setup("${AVND_TARGET}" "${AVND_FX_TARGET}") target_sources(Avendish PRIVATE diff --git a/cmake/avendish.sources.cmake b/cmake/avendish.sources.cmake index 889987dc..5b3126bd 100644 --- a/cmake/avendish.sources.cmake +++ b/cmake/avendish.sources.cmake @@ -72,7 +72,7 @@ function(avnd_target_setup AVND_FX_TARGET) target_compile_options( ${AVND_FX_TARGET} PUBLIC - -stdlib=libc++ + # -stdlib=libc++ # -flto -fno-stack-protector -fno-ident @@ -133,7 +133,7 @@ function(avnd_target_setup AVND_FX_TARGET) ) elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") target_link_libraries(${AVND_FX_TARGET} PRIVATE - -lc++ + # -lc++ -Bsymbolic # -flto ) diff --git a/cmake/avendish.standalone.cmake b/cmake/avendish.standalone.cmake index dcaf1bb3..8ca6bb26 100644 --- a/cmake/avendish.standalone.cmake +++ b/cmake/avendish.standalone.cmake @@ -3,7 +3,6 @@ if(CMAKE_SYSTEM_NAME MATCHES "WAS.*") endfunction() return() endif() -find_package(ossia) find_package(GLEW QUIET) find_package(glfw3 QUIET) find_package(OpenGL QUIET) @@ -86,7 +85,20 @@ function(avnd_make_standalone) ${AVND_FX_TARGET} PUBLIC ossia::ossia - SDL2 + ) + endif() + + if(TARGET SDL2::SDL2) + target_link_libraries( + ${AVND_FX_TARGET} + PUBLIC + SDL2::SDL2 + ) + elseif(TARGET SDL2::SDL2-static) + target_link_libraries( + ${AVND_FX_TARGET} + PUBLIC + SDL2::SDL2-static ) endif() @@ -148,6 +160,7 @@ function(avnd_make_standalone) ${AVND_FX_TARGET} PUBLIC Avendish::Avendish + jthread ) if(TARGET ossia::ossia) diff --git a/cmake/avendish.vst3.cmake b/cmake/avendish.vst3.cmake index 673620d7..2bfd6f6f 100644 --- a/cmake/avendish.vst3.cmake +++ b/cmake/avendish.vst3.cmake @@ -29,6 +29,15 @@ if(NOT MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-non-virtual-dtor") endif() +if(APPLE) + set(VST3_DYNAMIC_LIST "_BundleEntry\n_BundleExit\n_bundleEntry\n_bundleExit\n_GetPluginFactory") +elseif(WIN32) + set(VST3_DYNAMIC_LIST "AVND {\nlocal:*;\nglobal:InitDll;\nExitDll;\nGetPluginFactory;\n};\n") +else() + set(VST3_DYNAMIC_LIST "AVND {\n global:\n ModuleEntry;\n ModuleExit;\n GetPluginFactory;\n local:\n *;\n};\n") +endif() + +file(WRITE "${CMAKE_BINARY_DIR}/vst3_symbols" "${VST3_DYNAMIC_LIST}") add_subdirectory("${VST3_SDK_ROOT}" "${CMAKE_BINARY_DIR}/vst3_sdk") function(avnd_make_vst3) @@ -62,9 +71,42 @@ function(avnd_make_vst3) ${AVND_FX_TARGET} PUBLIC Avendish::Avendish_vst3 - sdk_common pluginterfaces + $,base sdk_common pluginterfaces,$> DisableExceptions ) + + if(UNIX AND NOT APPLE) + target_link_libraries( + ${AVND_FX_TARGET} + PUBLIC + -Wl,-z,defs + ) + endif() + + if(APPLE) + target_link_libraries(${AVND_FX_TARGET} PRIVATE jthread "-Wl,-exported_symbols_list,${CMAKE_BINARY_DIR}/vst3_symbols") + elseif(WIN32) + if(NOT MSVC) + target_link_libraries(${AVND_FX_TARGET} PRIVATE "-Wl,--version-script=${CMAKE_BINARY_DIR}/vst3_symbols") + endif() + else() + target_link_libraries(${AVND_FX_TARGET} PRIVATE "-Wl,--version-script=${CMAKE_BINARY_DIR}/vst3_symbols") + endif() + + + if(TARGET ossia::ossia) + target_compile_definitions( + ${AVND_FX_TARGET} + PUBLIC + AVND_ADD_OSCQUERY_BINDINGS=1 + ) + target_link_libraries( + ${AVND_FX_TARGET} + PUBLIC + ossia::ossia + ) + endif() + if(APPLE) find_library(COREFOUNDATION_FK CoreFoundation) target_link_libraries( diff --git a/examples/Helpers/Controls.hpp b/examples/Helpers/Controls.hpp index 71ba432b..4ddfa921 100644 --- a/examples/Helpers/Controls.hpp +++ b/examples/Helpers/Controls.hpp @@ -58,7 +58,7 @@ struct Controls struct range { - halp::combo_pair values[3]{{"Foo", 0.1f}, {"Bar", 0.5f}, {"Baz", 0.8f}}; + halp::combo_pair values[3]{{"A", 0.1f}, {"B", 0.5f}, {"C", 0.8f}}; int init{1}; // Bar }; diff --git a/include/avnd/binding/standalone/oscquery_mapper.hpp b/include/avnd/binding/standalone/oscquery_mapper.hpp index 1acae3bc..0e98743b 100644 --- a/include/avnd/binding/standalone/oscquery_mapper.hpp +++ b/include/avnd/binding/standalone/oscquery_mapper.hpp @@ -2,6 +2,8 @@ /* SPDX-License-Identifier: GPL-3.0-or-later */ +#include +#include #include #include #include @@ -16,9 +18,7 @@ #include #include #include -#include #include - namespace standalone { template @@ -35,13 +35,14 @@ struct oscquery_mapper std::shared_ptr m_context; ossia::net::generic_device m_dev; - explicit oscquery_mapper(avnd::effect_container& object, int osc_port, int ws_port) + explicit oscquery_mapper( + avnd::effect_container& object, std::string name, int osc_port, int ws_port) : object{object} , m_context{std::make_shared()} , m_dev{ std::make_unique( m_context, osc_port, ws_port), - "my_device"} + name} { create_ports(); } @@ -50,7 +51,7 @@ struct oscquery_mapper requires(!avnd::enum_parameter) void setup_control(Field& field, ossia::net::parameter_base& param) { - param.set_value_type(type_for_arg()); + param.set_value_type(oscr::type_for_arg()); // Set-up the metadata if constexpr(avnd::parameter_with_minmax_range) @@ -63,8 +64,10 @@ struct oscquery_mapper param.set_access(ossia::access_mode::BI); // Set-up the external callback - param.add_callback([&field](const ossia::value& val) { - field.value = convert(val, tag{}); + param.add_callback([object = &object, &field](const ossia::value& val) { + oscr::from_ossia_value(field, val, field.value); + + if_possible(field.update(object.effect)); }); } @@ -82,32 +85,9 @@ struct oscquery_mapper param.set_access(ossia::access_mode::BI); // Set-up the external callback - - param.add_callback([&field](const ossia::value& val) { - if(const int* iindex = val.target()) - { - if(*iindex >= 0 && *iindex < choices_count) - { - field.value = static_cast(*iindex); - } - } - else if(const float* findex = val.target()) - { - int index = *findex; - if(index >= 0 && index < choices_count) - { - field.value = static_cast(index); - } - } - else if(const std::string* txt = val.target()) - { - auto it = std::find(choices.begin(), choices.end(), *txt); - if(it != choices.end()) - { - int index = std::distance(choices.begin(), it); - field.value = static_cast(index); - } - } + param.add_callback([object = &object, &field](const ossia::value& val) { + oscr::from_ossia_value(field, val, field.value); + if_possible(field.update(object.effect)); }); } @@ -115,9 +95,7 @@ struct oscquery_mapper void create_control(Field& field) { ossia::net::node_base& node = m_dev.get_root_node(); - std::string name = "input"; - if constexpr(requires { Field::name(); }) - name = Field::name(); + std::string name{avnd::get_path(field)}; if(auto param = ossia::net::create_parameter(node, name)) @@ -158,7 +136,9 @@ struct oscquery_mapper template static Arg convert(const ossia::value& atom, tag) { - return ossia::convert(atom); + Arg a; + oscr::from_ossia_value(atom, a); + return a; } static std::string convert(const ossia::value& atom, tag) @@ -253,6 +233,10 @@ struct oscquery_mapper { f(object.effect, ossia::convert(in)); } + else if constexpr(std::is_same_v) + { + f(object.effect, ossia::convert(in)); + } else if constexpr(std::is_same_v>) { f(object.effect, ossia::convert>(in)); @@ -350,44 +334,6 @@ struct oscquery_mapper } } - template - static constexpr ossia::val_type type_for_arg() - { - if constexpr(std::floating_point) - { - return ossia::val_type::FLOAT; - } - else if constexpr(std::integral) - { - return ossia::val_type::INT; - } - else if constexpr(std::is_same_v) - { - return ossia::val_type::BOOL; - } - else if constexpr(std::is_same_v) - { - return ossia::val_type::STRING; - } - else if constexpr(std::is_same_v) - { - return ossia::val_type::STRING; - } - else if constexpr(std::is_same_v>) - { - return ossia::val_type::VEC2F; - } - else if constexpr(std::is_same_v>) - { - return ossia::val_type::VEC3F; - } - else if constexpr(std::is_same_v>) - { - return ossia::val_type::VEC4F; - } - return ossia::val_type::IMPULSE; - } - template void init_message_arguments( ossia::net::parameter_base& param, boost::mp11::mp_list) @@ -398,7 +344,7 @@ struct oscquery_mapper } else if constexpr(sizeof...(Args) == 1) { - param.set_value_type(type_for_arg()); + param.set_value_type(oscr::type_for_arg()); } else { @@ -406,10 +352,11 @@ struct oscquery_mapper init.reserve(sizeof...(Args)); param.set_value_type(ossia::val_type::LIST); - (init.push_back(ossia::init_value(type_for_arg())), ...); + (init.push_back(ossia::init_value(oscr::type_for_arg())), ...); param.set_value(std::move(init)); } } + template void init_message_arguments( ossia::net::parameter_base& param, boost::mp11::mp_list) @@ -439,7 +386,7 @@ struct oscquery_mapper if constexpr(requires { avnd::function_reflection::count; }) { ossia::net::node_base& node = m_dev.get_root_node(); - std::string name{Field::name()}; + std::string name{avnd::get_path(field)}; if(auto param = ossia::net::create_parameter( node, name)) // TODO { @@ -467,14 +414,12 @@ struct oscquery_mapper void create_output(Field& field) { ossia::net::node_base& node = m_dev.get_root_node(); - std::string name = "output"; - if constexpr(requires { Field::name(); }) - name = Field::name(); + std::string name{avnd::get_path(field)}; if(auto param = ossia::net::create_parameter(node, name)) { - param->set_value_type(type_for_arg()); + param->set_value_type(oscr::type_for_arg()); if constexpr(avnd::has_range) { constexpr auto ctl = avnd::get_range(); @@ -485,51 +430,32 @@ struct oscquery_mapper } } - template - void create_control(Field& field) - { - } - - template - void create_output(Field& field) - { - } - void create_ports() { - /* - if constexpr (avnd::float_parameter_input_introspection::size > 0) - { - avnd::for_each_field_ref( - object.inputs(), + if constexpr(avnd::has_inputs) + avnd::parameter_input_introspection::for_all( + avnd::get_inputs(object), [this](Field& f) { create_control(f); }); - } - if constexpr (avnd::float_parameter_output_introspection::size > 0) - { - avnd::for_each_field_ref( - object.outputs(), + if constexpr(avnd::has_outputs) + avnd::parameter_output_introspection::for_all( + avnd::get_outputs(object), [this](Field& f) { create_output(f); }); - } -*/ + if constexpr(avnd::has_messages) - { - avnd::for_each_field_ref( - avnd::get_messages(object), + avnd::messages_introspection::for_all( + avnd::get_messages(object), [&](Field& f) { create_message(f); }); - } - - /* - std::vector my_params; - for(int i = 0; i < 10; i++) - { - auto& node = find_or_create_node(m_dev, "/tes t/ fo o." + std::to_string(i)); - auto param = node.create_parameter(ossia::val_type::FLOAT); - param->push_value(0.1 + 0.01 * i); - my_params.push_back(param); - } - */ + // FIXME: for callbacks we must not overwrite the callback already set + // by the binding. + // Same thing for the UI. + // -> in the end, we need to have some multiplexing layer for when we have more than 1 binding + // + // if constexpr(avnd::has_outputs) + // avnd::callback_output_introspection::for_all( + // avnd::get_outputs(object), + // [&](Field& f) { create_callback(f); }); } void run() { m_context->run(); } diff --git a/include/avnd/binding/standalone/oscquery_minimal_mapper.hpp b/include/avnd/binding/standalone/oscquery_minimal_mapper.hpp new file mode 100644 index 00000000..906aa783 --- /dev/null +++ b/include/avnd/binding/standalone/oscquery_minimal_mapper.hpp @@ -0,0 +1,121 @@ +#pragma once + +/* SPDX-License-Identifier: GPL-3.0-or-later */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#if __has_include() +#include +#endif +namespace standalone +{ +template +struct oscquery_minimal_mapper +{ + using inputs_type = typename avnd::inputs_type::type; + std::shared_ptr m_context; + ossia::net::generic_device m_dev; + std::jthread m_thread; + + explicit oscquery_minimal_mapper(std::string name, int osc_port, int ws_port) + : m_context{std::make_shared()} + , m_dev{ + std::make_unique( + m_context), + name} + { + m_thread = std::jthread([this] (std::stop_token tk){ + std::this_thread::sleep_for(std::chrono::seconds(1)); + while(!tk.stop_requested()) + { + std::cerr << ("Starting...") << std::endl; + m_context->run(); + } + std::cerr << ("Exited") << std::endl; + }); + } + + ~oscquery_minimal_mapper() + { + std::cerr <<("oh shit"); + } + + template + requires(!avnd::enum_parameter) + void setup_control(Field& field, ossia::net::parameter_base& param) + { + std::cerr << ("Adding a control") << avnd::get_name() << std::endl; + param.set_value_type(oscr::type_for_arg()); + + // Set-up the metadata + if constexpr(avnd::parameter_with_minmax_range) + { + constexpr auto ctl = avnd::get_range(); + param.set_domain(ossia::make_domain(ctl.min, ctl.max)); + param.set_value(ctl.init); + } + + param.set_access(ossia::access_mode::BI); + + // Set-up the external callback + param.add_callback([&field](const ossia::value& val) { + oscr::from_ossia_value(field, val, field.value); + // FIXME callback into the origin + }); + } + + template + void setup_control(Field& field, ossia::net::parameter_base& param) + { + param.set_value_type(ossia::val_type::STRING); + + static constexpr const auto choices = avnd::get_enum_choices(); + static constexpr const auto choices_count = choices.size(); + ossia::domain_base dom{{choices.begin(), choices.end()}}; + // Set-up the metadata + param.set_value(dom.values[(int)avnd::get_range().init]); + param.set_domain(std::move(dom)); + param.set_access(ossia::access_mode::BI); + + // Set-up the external callback + param.add_callback([&field](const ossia::value& val) { + oscr::from_ossia_value(field, val, field.value); + }); + } + + template + void create_control(Field& field) + { + ossia::net::node_base& node = m_dev.get_root_node(); + std::string name{avnd::get_path(field)}; + + if(auto param + = ossia::net::create_parameter(node, name)) + { + setup_control(field, *param); + } + } + + void create_ports(typename avnd::inputs_type::type& inputs) + { + avnd::parameter_input_introspection::for_all(inputs, + [this](Field& f) { create_control(f); }); + } +}; +} diff --git a/include/avnd/binding/standalone/prototype.cpp.in b/include/avnd/binding/standalone/prototype.cpp.in index c21a5fa5..ff18d55f 100644 --- a/include/avnd/binding/standalone/prototype.cpp.in +++ b/include/avnd/binding/standalone/prototype.cpp.in @@ -22,18 +22,18 @@ void run_ui(auto& object) #if AVND_STANDALONE_NKL if constexpr(avnd::has_ui) { - nkl::layout_ui< type > ui{object}; + nkl::layout_ui ui{object}; ui.render(); } #elif AVND_STANDALONE_QML if constexpr(avnd::has_ui) { - qml::qml_layout_ui< type > ui{object}; + qml::qml_layout_ui ui{object}; qApp->exec(); } else { - qml::qml_ui< type > ui{object}; + qml::qml_ui ui{object}; qApp->exec(); } #endif @@ -48,6 +48,7 @@ int main(int argc, char** argv) int osc_port = 1234; int ws_port = 5678; + const auto app_name = std::string(avnd::get_name()); #if AVND_STANDALONE_QML qputenv("QML_DISABLE_DISTANCEFIELD", "1"); qputenv("QT_SCALE_FACTOR", "1"); @@ -55,8 +56,7 @@ int main(int argc, char** argv) qputenv("QT_QUICK_CONTROLS_STYLE", "Material"); qputenv("QT_QUICK_CONTROLS_MATERIAL_THEME", "Dark"); - QCoreApplication::setApplicationName( - QString::fromStdString(std::string(avnd::get_name()))); + QCoreApplication::setApplicationName(QString::fromStdString(app_name)); QCoreApplication::setApplicationVersion( QString::fromStdString(std::string(avnd::get_version()))); QCoreApplication::setOrganizationName( @@ -119,18 +119,20 @@ int main(int argc, char** argv) avnd::init_controls(object); // Create an audio processor -#if __has_include() +#if AVND_STANDALONE_AUDIO standalone::audio_mapper audio{ object, in_channels, out_channels, buffer_size, sample_rate}; +#endif +#if AVND_STANDALONE_OSCQUERY // Create an oscquery interface to it. - standalone::oscquery_mapper oscq{object, osc_port, ws_port}; + standalone::oscquery_mapper oscq{object, app_name, osc_port, ws_port}; std::thread t{[&] { oscq.run(); }}; #endif run_ui(object); -#if __has_include() +#if AVND_STANDALONE_OSCQUERY oscq.stop(); t.join(); #endif diff --git a/include/avnd/binding/standalone/standalone.hpp b/include/avnd/binding/standalone/standalone.hpp index c90517d5..a9749442 100644 --- a/include/avnd/binding/standalone/standalone.hpp +++ b/include/avnd/binding/standalone/standalone.hpp @@ -2,13 +2,15 @@ /* SPDX-License-Identifier: GPL-3.0-or-later */ -#if __has_include() +#if __has_include() #include +#define AVND_STANDALONE_AUDIO 1 +#endif + #if __has_include() #define AVND_STANDALONE_OSCQUERY 1 #include #endif -#endif #if __has_include() && __has_include() #define AVND_STANDALONE_QML 1 diff --git a/include/avnd/binding/vst3/controller.hpp b/include/avnd/binding/vst3/controller.hpp index b09fbd3e..04fb71e5 100644 --- a/include/avnd/binding/vst3/controller.hpp +++ b/include/avnd/binding/vst3/controller.hpp @@ -14,6 +14,9 @@ #include +#if defined(AVND_ADD_OSCQUERY_BINDINGS) +#include +#endif namespace stv3 { template @@ -60,12 +63,22 @@ class Controller final using inputs_t = typename avnd::inputs_type::type; inputs_t inputs_mirror{}; +#undef AVND_ADD_OSCQUERY_BINDINGS +#if AVND_ADD_OSCQUERY_BINDINGS + standalone::oscquery_minimal_mapper oscquery_mapper{ + std::string(avnd::get_name()), 1234, 5678}; +#endif using inputs_info_t = avnd::parameter_input_introspection; static const constexpr int32_t parameter_count = inputs_info_t::size; public: - Controller() { } + Controller() + { +#if AVND_ADD_OSCQUERY_BINDINGS + oscquery_mapper.create_ports(inputs_mirror); +#endif + } virtual ~Controller(); @@ -170,7 +183,6 @@ class Controller final { bool ok = inputs_info_t::for_all_unless( this->inputs_mirror, [&](C& field) -> bool { - double param = 0.f; if(streamer.readDouble(param) == false) return false;