Loading...
Searching...
No Matches
AddressTools.hpp
1#pragma once
2#include <ossia/dataflow/exec_state_facade.hpp>
3#include <ossia/dataflow/execution_state.hpp>
4#include <ossia/detail/algorithms.hpp>
5#include <ossia/network/base/device.hpp>
6#include <ossia/network/common/path.hpp>
7
8#include <boost/algorithm/string.hpp>
9
10#include <AvndProcesses/Alphanum.hpp>
11#include <halp/controls.hpp>
12#include <halp/meta.hpp>
13
14namespace avnd_tools
15{
16
17// Given:
18// address: /foo.*/value
19// -> /foo.1/value, /foo.2/value, etc.
20// input: [1, 34, 6, 4]
21// -> writes 1 on foo.1/value, 34 on foo.2/value, etc
22
24{
25 ossia::exec_state_facade ossia_state;
26 std::vector<ossia::traversal::path> m_paths;
27 std::vector<ossia::net::node_base*> roots;
28};
29
30// Watches the execution devices for tree changes. A Nano::Observer, so that
31// its connections go away with whichever of it or the device dies first: the
32// devices it stops observing (no longer executed, possibly already destroyed)
33// cannot keep a slot pointing at a freed selector. A copy starts unconnected.
34struct PatternDeviceObserver : Nano::Observer
35{
36 PatternDeviceObserver() = default;
38 PatternDeviceObserver& operator=(const PatternDeviceObserver&) noexcept { return *this; }
39
40 void mark_dirty(ossia::net::node_base&) { dirty = true; }
41
42 bool dirty = false;
43 ossia::flat_set<ossia::net::device_base*> devices;
44};
45
46struct PatternSelector : halp::lineedit<"Pattern", "">
47{
48 void update(PatternObject& p)
49 {
50 current_object = &p;
51 if(!p.ossia_state.impl)
52 return;
53 if(value.empty())
54 {
55 p.m_paths.clear();
56 p.roots.clear();
57 return;
58 }
59
60 p.m_paths.clear();
61 std::vector<std::string> results;
62 boost::split(results, value, [](char c){ return c == '\n' || c == '\r'; });
63
64 for(auto& res : results) {
65 if(!res.empty())
66 if(auto path = ossia::traversal::make_path(res))
67 {
68 p.m_paths.push_back(std::move(*path));
69 }
70 }
71
72 reprocess();
73 }
74
75 void reprocess()
76 {
77 if(!current_object)
78 return;
79
80 auto& p = *current_object;
81 ossia::execution_state& st = *p.ossia_state.impl;
82 const auto& rdev = st.exec_devices();
83 p.roots.clear();
84
85 if(p.m_paths.empty())
86 return;
87
88 auto& observed = observer.devices;
89 for(auto& dev : rdev)
90 {
91 if(observed.find(dev) == observed.end())
92 {
93 observed.insert(dev);
94 dev->on_node_created.connect<&PatternDeviceObserver::mark_dirty>(observer);
95 dev->on_node_removing.connect<&PatternDeviceObserver::mark_dirty>(observer);
96 }
97 p.roots.push_back(&dev->get_root_node());
98 }
99
100 // Not disconnected here: such a device may already be gone. A live one
101 // stays connected until the observer dies, which is harmless.
102 for(auto it = observed.begin(); it != observed.end();)
103 {
104 if(!ossia::contains(rdev, *it))
105 it = observed.erase(it);
106 else
107 ++it;
108 }
109
110 const auto orig_roots = p.roots;
111 std::vector<ossia::net::node_base*> result;
112 std::vector<ossia::net::node_base*> total;
113 for(auto& path : p.m_paths) {
114 result.assign(orig_roots.begin(), orig_roots.end());
115
116 ossia::traversal::apply(path, result);
117 total.insert(total.end(), result.begin(), result.end());
118 }
119
120 ossia::remove_duplicates(total); // First remove pointer duplicates
121 ossia::sort( // Then alphanum sort
122 total,
123 [](ossia::net::node_base* lhs, ossia::net::node_base* rhs) {
124 return doj::alphanum_compare{}(lhs->osc_address(), rhs->osc_address());
125 });
126
127 std::swap(p.roots, total);
128
129 observer.dirty = false;
130 }
131
132 PatternObject* current_object{};
133 PatternDeviceObserver observer;
134};
135
137{
138 halp_meta(name, "Pattern applier")
139 halp_meta(author, "ossia team")
140 halp_meta(category, "Control/Data Processing")
141 halp_meta(description, "Send a message to all nodes matching a pattern")
142 halp_meta(c_name, "avnd_pattern_apply")
143 halp_meta(uuid, "44a55ee1-c2c9-43d5-a655-8eaedaff394c")
144 halp_meta(manual_url, "https://ossia.io/score-docs/processes/pattern-applier.html#pattern-applier")
145
146 struct
147 {
148 halp::val_port<"Input", ossia::value> input;
149 PatternSelector pattern;
150 } inputs;
151
152 struct
153 {
154
155 } outputs;
156
157 void operator()()
158 {
159 if(m_paths.empty())
160 return;
161
162 auto process = [this](const std::vector<ossia::value>& vec) {
163 const auto N = std::min(roots.size(), vec.size());
164 for(std::size_t i = 0; i < N; i++)
165 {
166 if(auto p = roots[i]->get_parameter())
167 {
168 p->push_value(vec[i]);
169 }
170 }
171 };
172
173 if(auto vvec = inputs.input.value.target<std::vector<ossia::value>>())
174 {
175 process(*vvec);
176 }
177 else
178 {
179 process(ossia::convert<std::vector<ossia::value>>(inputs.input.value));
180 }
181 }
182};
183}
Definition AddressTools.hpp:35
Definition AddressTools.hpp:24
Definition AddressTools.hpp:47
Definition AddressTools.hpp:137
Definition Alphanum.hpp:28