From 21f0a8df72869e696d2646f9343ecf83c8b0cd77 Mon Sep 17 00:00:00 2001 From: Yan Pujante Date: Fri, 14 Apr 2023 10:47:20 -0700 Subject: [PATCH] fixed multi bindings/same source in rtc_bindings --- CMakeLists.txt | 2 +- README.md | 4 ++ src/cpp/re/mock/Motherboard.cpp | 25 +++++--- src/cpp/re/mock/Motherboard.h | 2 +- src/cpp/re/mock/lua/RealtimeController.cpp | 8 ++- src/cpp/re/mock/lua/RealtimeController.h | 4 +- test/cpp/re/mock/TestRackExtension.cpp | 64 +++++++++++++++++++ .../re/mock/lua/TestRealtimeController.cpp | 2 +- ...nsion_RealtimeController_MultiBindings.lua | 38 +++++++++++ 9 files changed, 133 insertions(+), 16 deletions(-) create mode 100644 test/resources/re/mock/lua/RackExtension_RealtimeController_MultiBindings.lua diff --git a/CMakeLists.txt b/CMakeLists.txt index 4146ca3..9d6f04f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,7 @@ cmake_minimum_required(VERSION 3.17) project(re-mock LANGUAGES CXX) set(re-mock_VERSION_MAJOR 1) set(re-mock_VERSION_MINOR 4) -set(re-mock_VERSION_PATCH 0) +set(re-mock_VERSION_PATCH 1) set(re-mock_VERSION "${re-mock_VERSION_MAJOR}.${re-mock_VERSION_MINOR}.${re-mock_VERSION_PATCH}") # Using C++17 diff --git a/README.md b/README.md index 42bc3e8..687fe9b 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,10 @@ Links Release notes ------------- +#### 1.4.1 - 2023-04-14 + +- Fixed issue when the same `source` was defined multiple times (to different `dest`) in `rtc_bindings` in `realtime_controler.lua` + #### 1.4.0 - 2023-04-11 - Use miniaudio instead of libsndfile for loading/saving audio files: this makes generating the CMake project and compiling much faster. As a result the CMake option `RE_MOCK_SUPPORT_FOR_AUDIO_FILE` has been removed entirely. diff --git a/src/cpp/re/mock/Motherboard.cpp b/src/cpp/re/mock/Motherboard.cpp index 2b18efd..90cb9ec 100644 --- a/src/cpp/re/mock/Motherboard.cpp +++ b/src/cpp/re/mock/Motherboard.cpp @@ -557,10 +557,13 @@ void Motherboard::init() } // rtc_bindings - for(auto const &[propertyPath, bindingKey]: fRealtimeController->getBindings()) + for(auto const &[propertyPath, bindingKeys]: fRealtimeController->getBindings()) { - auto diff = registerRTCBinding(propertyPath, bindingKey); - fRealtimeController->invokeBinding(this, bindingKey, getPropertyPath(diff.fPropertyRef), diff.fCurrentValue); + for(auto const &bindingKey: bindingKeys) + { + auto diff = registerRTCBinding(propertyPath, bindingKey); + fRealtimeController->invokeBinding(this, bindingKey, getPropertyPath(diff.fPropertyRef), diff.fCurrentValue); + } } } @@ -768,7 +771,9 @@ void Motherboard::registerRTCNotify(std::string const &iPropertyPath) impl::JboxPropertyDiff Motherboard::registerRTCBinding(std::string const &iPropertyPath, std::string const &iBindingKey) { auto ref = getPropertyRef(iPropertyPath); - fRTCBindings[ref] = iBindingKey; + if(!stl::contains_key(fRTCBindings, ref)) + fRTCBindings[ref] = {}; + fRTCBindings[ref].emplace(iBindingKey); return fJboxObjects.get(ref.fObject)->watchPropertyForChange(ref.fKey); } @@ -864,10 +869,14 @@ void Motherboard::nextBatch() for(auto &diff : diffs) { - fRealtimeController->invokeBinding(this, - fRTCBindings.at(diff.fPropertyRef), - getPropertyPath(diff.fPropertyRef), - diff.fCurrentValue); + auto const &bindingKeys = fRTCBindings.at(diff.fPropertyRef); + for(auto binding: bindingKeys) + { + fRealtimeController->invokeBinding(this, + binding, + getPropertyPath(diff.fPropertyRef), + diff.fCurrentValue); + } } // next we call render_realtime diff --git a/src/cpp/re/mock/Motherboard.h b/src/cpp/re/mock/Motherboard.h index cdbc717..7736ec3 100644 --- a/src/cpp/re/mock/Motherboard.h +++ b/src/cpp/re/mock/Motherboard.h @@ -349,7 +349,7 @@ class Motherboard std::set fRTCNotify{compare}; std::vector fRTCNotifyDiffs{}; bool fRTCNotifyEnabled{true}; - std::map fRTCBindings{compare}; + std::map, ComparePropertyRef> fRTCBindings{compare}; std::vector fRTCBindingsDiffs{}; bool fRTCBindingsEnabled{true}; std::vector fUserSamplePropertyPaths{}; diff --git a/src/cpp/re/mock/lua/RealtimeController.cpp b/src/cpp/re/mock/lua/RealtimeController.cpp index a9e91d3..fa86a65 100644 --- a/src/cpp/re/mock/lua/RealtimeController.cpp +++ b/src/cpp/re/mock/lua/RealtimeController.cpp @@ -476,11 +476,11 @@ void RealtimeController::invokeBinding(Motherboard *iMotherboard, //------------------------------------------------------------------------ // RealtimeController::getBindings //------------------------------------------------------------------------ -std::map const &RealtimeController::getBindings() +std::map> const &RealtimeController::getBindings() { if(!fBindings) { - std::map bindings{}; + std::map> bindings{}; if(lua_getglobal(L, "rtc_bindings") != LUA_TNIL) { auto mapIndex = lua_gettop(L); @@ -495,7 +495,9 @@ std::map const &RealtimeController::getBindings() dest = dest.substr(12); // skip /global_rtc/ putBindingOnTopOfStack(dest); // this will check that the binding exists lua_pop(L, 1); - bindings[source] = dest; + if(!stl::contains_key(bindings, source)) + bindings[source] = {}; + bindings[source].emplace(dest); // also add it to reverse bindings if(!stl::contains_key(fReverseBindings, dest)) diff --git a/src/cpp/re/mock/lua/RealtimeController.h b/src/cpp/re/mock/lua/RealtimeController.h index 7798e52..f52212b 100644 --- a/src/cpp/re/mock/lua/RealtimeController.h +++ b/src/cpp/re/mock/lua/RealtimeController.h @@ -57,7 +57,7 @@ class RealtimeController: public MockJBox int luaGetSampleInfo(); int luaGetSampleMetaData(); - std::map const &getBindings(); + std::map> const &getBindings(); std::set getRTInputSetupNotify(); void invokeBinding(Motherboard *iMotherboard, @@ -80,7 +80,7 @@ class RealtimeController: public MockJBox Motherboard *fMotherboard{}; std::string fCurrentBindingName{}; ObjectManager> fJboxValues{}; - std::optional> fBindings{}; + std::optional>> fBindings{}; std::map> fReverseBindings{}; }; diff --git a/test/cpp/re/mock/TestRackExtension.cpp b/test/cpp/re/mock/TestRackExtension.cpp index 32570d0..234ee5c 100644 --- a/test/cpp/re/mock/TestRackExtension.cpp +++ b/test/cpp/re/mock/TestRackExtension.cpp @@ -385,6 +385,70 @@ TEST(RackExtension, Motherboard_NativeObject) ASSERT_EQ("abc", o->fStringValue); } +// RackExtension.RealtimeController_MultiBindings +TEST(RackExtension, RealtimeController_MultiBindings) +{ + Rack rack{}; + + auto c = DeviceConfig::fromSkeleton() + .mdef(Config::document_owner_property("source_1", lua::jbox_string_property{})) + .mdef(Config::document_owner_property("source_2", lua::jbox_string_property{})) + .mdef(Config::rtc_owner_property("source_1_return", lua::jbox_string_property{})) + .mdef(Config::rtc_owner_property("any_source_1_return", lua::jbox_string_property{})) + .mdef(Config::rtc_owner_property("any_source_2_return", lua::jbox_string_property{})) + + .rtc(resource::File{fs::path(RE_MOCK_PROJECT_DIR) / "test" / "resources" / "re" / "mock" / "lua" / + "RackExtension_RealtimeController_MultiBindings.lua"}) + ; + + auto re = rack.newDevice(c); + + ASSERT_EQ("", re.getString("/custom_properties/source_1")); + ASSERT_EQ("", re.getString("/custom_properties/source_2")); + ASSERT_EQ("", re.getString("/custom_properties/source_1_return")); + ASSERT_EQ("", re.getString("/custom_properties/any_source_1_return")); + ASSERT_EQ("", re.getString("/custom_properties/any_source_2_return")); + + rack.nextBatch(); + + ASSERT_EQ("", re.getString("/custom_properties/source_1")); + ASSERT_EQ("", re.getString("/custom_properties/source_2")); + ASSERT_EQ("", re.getString("/custom_properties/source_1_return")); + ASSERT_EQ("", re.getString("/custom_properties/any_source_1_return")); + ASSERT_EQ("", re.getString("/custom_properties/any_source_2_return")); + + re.setString("/custom_properties/source_1", "s1"); + + rack.nextBatch(); + + ASSERT_EQ("s1", re.getString("/custom_properties/source_1")); + ASSERT_EQ("", re.getString("/custom_properties/source_2")); + ASSERT_EQ("s1", re.getString("/custom_properties/source_1_return")); + ASSERT_EQ("s1", re.getString("/custom_properties/any_source_1_return")); + ASSERT_EQ("", re.getString("/custom_properties/any_source_2_return")); + + re.setString("/custom_properties/source_2", "s2"); + + rack.nextBatch(); + + ASSERT_EQ("s1", re.getString("/custom_properties/source_1")); + ASSERT_EQ("s2", re.getString("/custom_properties/source_2")); + ASSERT_EQ("s1", re.getString("/custom_properties/source_1_return")); + ASSERT_EQ("s1", re.getString("/custom_properties/any_source_1_return")); + ASSERT_EQ("s2", re.getString("/custom_properties/any_source_2_return")); + + re.setString("/custom_properties/source_1", "s1.2"); + re.setString("/custom_properties/source_2", "s2.2"); + + rack.nextBatch(); + + ASSERT_EQ("s1.2", re.getString("/custom_properties/source_1")); + ASSERT_EQ("s2.2", re.getString("/custom_properties/source_2")); + ASSERT_EQ("s1.2", re.getString("/custom_properties/source_1_return")); + ASSERT_EQ("s1.2", re.getString("/custom_properties/any_source_1_return")); + ASSERT_EQ("s2.2", re.getString("/custom_properties/any_source_2_return")); +} + // RackExtension.RealtimeController_NativeObject TEST(RackExtension, RealtimeController_NativeObject) { diff --git a/test/cpp/re/mock/lua/TestRealtimeController.cpp b/test/cpp/re/mock/lua/TestRealtimeController.cpp index 74e23d1..bf2120b 100644 --- a/test/cpp/re/mock/lua/TestRealtimeController.cpp +++ b/test/cpp/re/mock/lua/TestRealtimeController.cpp @@ -49,7 +49,7 @@ TEST(RealtimeController, BlankEffect) ASSERT_EQ(def->getStackString(), ""); ASSERT_EQ(def->getBindings().size(), 1); - ASSERT_EQ("init_instance", def->getBindings().at("/environment/system_sample_rate")); + ASSERT_EQ(std::set{"init_instance"}, def->getBindings().at("/environment/system_sample_rate")); ASSERT_EQ(def->getRTInputSetupNotify().size(), 4); ASSERT_THAT(def->getRTInputSetupNotify(), UnorderedElementsAre("/audio_inputs/MainInLeft/connected", diff --git a/test/resources/re/mock/lua/RackExtension_RealtimeController_MultiBindings.lua b/test/resources/re/mock/lua/RackExtension_RealtimeController_MultiBindings.lua new file mode 100644 index 0000000..7ce9c5a --- /dev/null +++ b/test/resources/re/mock/lua/RackExtension_RealtimeController_MultiBindings.lua @@ -0,0 +1,38 @@ +format_version = "1.0" + +rtc_bindings = { + -- this will initialize the C++ object + { source = "/environment/system_sample_rate", dest = "/global_rtc/init_instance" }, + { source = "/custom_properties/source_1", dest = "/global_rtc/on_source_1" }, + { source = "/custom_properties/source_1", dest = "/global_rtc/on_any_source" }, + { source = "/custom_properties/source_2", dest = "/global_rtc/on_any_source" }, +} + +global_rtc = { + init_instance = function(source_property_path, new_value) + local sample_rate = jbox.load_property("/environment/system_sample_rate") + local new_no = jbox.make_native_object_rw("Instance", { sample_rate }) + jbox.store_property("/custom_properties/instance", new_no) + end, + + on_source_1 = function(source_property_path, new_value) + --print("from on_source_1 " .. tostring(new_value)) + jbox.store_property("/custom_properties/source_1_return", new_value) + end, + + on_any_source = function(source_property_path, new_value) + --print("from on_any_source(" .. tostring(source_property_path) .. "," .. tostring(new_value) .. ")") + -- allowed because both are declared as sources of the callback + local s1_value = jbox.load_property("/custom_properties/source_1") + local s2_value = jbox.load_property("/custom_properties/source_2") + if source_property_path == "/custom_properties/source_1" then + assert(s1_value == new_value) + jbox.store_property("/custom_properties/any_source_1_return", new_value) + end + if source_property_path == "/custom_properties/source_2" then + assert(s2_value == new_value) + jbox.store_property("/custom_properties/any_source_2_return", new_value) + end + end, + +}