OSSIA
Open Scenario System for Interactive Application
ossia-pd/src/utils.hpp
1 #pragma once
2 
3 #include <ossia/detail/fmt.hpp>
5 #include <ossia/network/domain/domain.hpp>
6 
7 #include <ossia-pd/src/client.hpp>
8 #include <ossia-pd/src/model.hpp>
9 #include <ossia-pd/src/object_base.hpp>
10 #include <ossia-pd/src/ossia-pd.hpp>
11 #include <ossia-pd/src/parameter.hpp>
12 #include <ossia-pd/src/remote.hpp>
13 #include <ossia-pd/src/view.hpp>
14 
15 #include <chrono>
16 #include <iostream>
17 
18 namespace ossia::pd
19 {
20 
21 template <typename TimeT = std::chrono::microseconds>
22 struct measure
23 {
24  template <typename F, typename... Args>
25  static typename TimeT::rep execution(F&& func, Args&&... args)
26  {
27  auto start = std::chrono::steady_clock::now();
28  // std::invoke(std::forward<decltype(func)>(func), std::forward<Args>(args)...);
29  std::forward<decltype(func)>(func)(std::forward<Args>(args)...);
30  auto duration
31  = std::chrono::duration_cast<TimeT>(std::chrono::steady_clock::now() - start);
32  return duration.count();
33  }
34 };
35 
36 #pragma mark Value type conversion helper
37 
38 struct value2atom
39 {
40  std::vector<t_atom>& data;
41  void operator()(impulse) const
42  {
43  t_atom a;
44  SETSYMBOL(&a, gensym("bang"));
45  data.push_back(a);
46  }
47 
48  void operator()(int32_t i) const
49  {
50  t_atom a;
51  SETFLOAT(&a, (t_float)i);
52  data.push_back(a);
53  }
54 
55  void operator()(float f) const
56  {
57  t_atom a;
58  SETFLOAT(&a, f);
59  data.push_back(a);
60  }
61 
62  void operator()(bool b) const
63  {
64  t_atom a;
65  t_float f = b ? 1. : 0.;
66  SETFLOAT(&a, f);
67  data.push_back(a);
68  }
69 
70  void operator()(const std::string& str) const
71  {
72  t_symbol* s = gensym(str.c_str());
73  t_atom a;
74  SETSYMBOL(&a, s);
75  data.push_back(a);
76  }
77 
78  void operator()(char c) const
79  {
80  t_atom a;
81  SETFLOAT(&a, (float)c);
82  data.push_back(a);
83  }
84 
85  template <std::size_t N>
86  void operator()(std::array<float, N> vec) const
87  {
88  data.reserve(data.size() + N);
89  for(std::size_t i = 0; i < N; i++)
90  {
91  t_atom a;
92  SETFLOAT(&a, vec[i]);
93  data.push_back(a);
94  }
95  }
96 
97  void operator()(const std::vector<ossia::value>& t) const
98  {
99  data.reserve(data.size() + t.size());
100  for(const auto& v : t)
101  v.apply(*this);
102  }
103 
104  void operator()(const ossia::value_map_type& t) const
105  {
106  // FIXME
107  }
108 
109  void operator()() const { }
110 };
111 
112 template <typename T>
113 struct value_visitor
114 {
115  T* x;
116 
117  void operator()(impulse) const
118  {
119  // TODO how to deal with impulse ? in Pd bang object doesn't have [set ...(
120  // message
121  // and sending a bang to the bang object connected to the inlet of the
122  // sender will lead to stack overflow...
123  if(x->m_dataout)
124  outlet_bang(x->m_dataout);
125 
126  if(x->m_setout)
127  outlet_bang(x->m_setout);
128  }
129  void operator()(int32_t i) const
130  {
131  t_atom a;
132  SETFLOAT(&a, (t_float)i);
133  if(x->m_dataout)
134  outlet_float(x->m_dataout, (t_float)i);
135 
136  if(x->m_setout)
137  outlet_anything(x->m_setout, ossia_pd::o_sym_set, 1, &a);
138  }
139  void operator()(float f) const
140  {
141  t_atom a;
142  SETFLOAT(&a, f);
143  if(x->m_dataout)
144  outlet_float(x->m_dataout, (t_float)f);
145 
146  if(x->m_setout)
147  outlet_anything(x->m_setout, ossia_pd::o_sym_set, 1, &a);
148  }
149  void operator()(bool b) const
150  {
151  t_atom a;
152  t_float f = b ? 1. : 0.;
153  SETFLOAT(&a, f);
154  if(x->m_dataout)
155  outlet_float(x->m_dataout, f);
156 
157  if(x->m_setout)
158  outlet_anything(x->m_setout, ossia_pd::o_sym_set, 1, &a);
159  }
160  void operator()(const std::string& str) const
161  {
162  t_symbol* s = gensym(str.c_str());
163  t_atom a;
164  SETSYMBOL(&a, s);
165  if(x->m_dataout)
166  outlet_symbol(x->m_dataout, s);
167 
168  if(x->m_setout)
169  outlet_anything(x->m_setout, ossia_pd::o_sym_set, 1, &a);
170  }
171 
172  template <std::size_t N>
173  void operator()(std::array<float, N> vec) const
174  {
175  t_atom a[N];
176 
177  for(std::size_t i = 0; i < N; i++)
178  {
179  SETFLOAT(a + i, vec[i]);
180  }
181 
182  if(x->m_dataout)
183  outlet_list(x->m_dataout, gensym("list"), N, a);
184 
185  if(x->m_setout)
186  outlet_anything(x->m_setout, ossia_pd::o_sym_set, N, a);
187  }
188 
189  void operator()(const std::vector<ossia::value>& t) const
190  {
191  std::vector<t_atom> va;
192  value2atom vm{va};
193 
194  if(t.empty())
195  return;
196 
197  for(const auto& v : t)
198  v.apply(vm);
199 
200  t_atom* list_ptr = !va.empty() ? va.data() : nullptr;
201  if(x->m_dataout)
202  outlet_list(x->m_dataout, gensym("list"), va.size(), list_ptr);
203 
204  if(x->m_setout)
205  outlet_anything(x->m_setout, ossia_pd::o_sym_set, va.size(), list_ptr);
206  }
207 
208  void operator()(const ossia::value_map_type& t) const { }
209 
210  void operator()() const { pd_error(x, "%s received invalid data", x->m_name->s_name); }
211 };
212 
213 struct domain_visitor
214 {
215  parameter_base* x;
216 
217  template <typename T>
218  void operator()(ossia::domain_base<T>& d)
219  {
220  if(d.min && d.max)
221  {
222  x->m_range_size = 2;
223  SETFLOAT(x->m_range, *d.min);
224  SETFLOAT(x->m_range + 1, *d.max);
225  }
226 
227  if(d.min)
228  {
229  x->m_min_size = 1;
230  SETFLOAT(x->m_min, *d.min);
231  }
232 
233  if(d.max)
234  {
235  x->m_max_size = 1;
236  SETFLOAT(x->m_max, *d.max);
237  }
238  }
239 
240  void operator()(ossia::domain_base<bool>& d)
241  {
242  // nothing to do
243  }
244 
245  void operator()(ossia::domain_base<impulse>& d)
246  {
247  // nothing to do
248  }
249  void operator()(ossia::domain_base<std::string> d)
250  {
251  if(!d.values.empty())
252  {
253  x->m_range_size = d.values.size() > OSSIA_PD_MAX_ATTR_SIZE ? OSSIA_PD_MAX_ATTR_SIZE
254  : d.values.size();
255  for(int i = 0; i < x->m_range_size; i++)
256  {
257  // SETSYMBOL(x->m_range+i,gensym(d.values[i].c_str()));
258  }
259  }
260  }
261  void operator()(ossia::domain_base<ossia::value> d)
262  {
263  // TODO
264  if(d.min)
265  {
266  }
267  if(d.max)
268  {
269  }
270  if(!d.values.empty())
271  {
272  }
273  }
274 
275  template <std::size_t N>
276  void operator()(ossia::vecf_domain<N>& d)
277  {
278  x->m_min_size
279  = d.min.size() > OSSIA_PD_MAX_ATTR_SIZE ? OSSIA_PD_MAX_ATTR_SIZE : d.min.size();
280  x->m_max_size
281  = d.max.size() > OSSIA_PD_MAX_ATTR_SIZE ? OSSIA_PD_MAX_ATTR_SIZE : d.max.size();
282 
283  for(int i = 0; i < x->m_max_size; i++)
284  atom_setfloat(&x->m_max[i], *d.max[i]);
285 
286  for(int i = 0; i < x->m_min_size; i++)
287  atom_setfloat(&x->m_min[i], *d.min[i]);
288 
289  x->m_range_size = 0;
290  if(x->m_min_size == x->m_max_size && x->m_min_size > 1)
291  {
292  bool flag = true;
293  for(int i = 1; i < x->m_min_size && flag; i++)
294  {
295  flag |= *d.min[0] == *d.min[i];
296  flag |= *d.max[0] == *d.max[i];
297  if(!flag)
298  break;
299  }
300  if(flag)
301  {
302  x->m_range_size = 2;
303  atom_setfloat(&x->m_range[0], *d.min[0]);
304  atom_setfloat(&x->m_range[1], *d.max[0]);
305  }
306  }
307  }
308 
309  void operator()(ossia::vector_domain& d)
310  {
311  std::vector<t_atom> vamin, vamax;
312  value2atom minvisitor{vamin};
313  value2atom maxvisitor{vamax};
314 
315  x->m_min_size
316  = vamin.size() > OSSIA_PD_MAX_ATTR_SIZE ? OSSIA_PD_MAX_ATTR_SIZE : vamin.size();
317  x->m_max_size
318  = vamax.size() > OSSIA_PD_MAX_ATTR_SIZE ? OSSIA_PD_MAX_ATTR_SIZE : vamax.size();
319 
320  for(const auto& v : d.min)
321  v.apply(minvisitor);
322  for(unsigned int i = 0; i < vamin.size(); i++)
323  x->m_min[i] = vamin[i];
324 
325  for(const auto& v : d.max)
326  v.apply(maxvisitor);
327  for(unsigned int i = 0; i < vamax.size(); i++)
328  x->m_max[i] = vamax[i];
329 
330  // TODO range
331  x->m_range_size = 0;
332  }
333  void operator()() { }
334 };
335 
336 ossia::value atom2value(t_symbol* s, int argc, t_atom* argv);
337 
338 #pragma mark Prototype
339 
340 std::vector<std::string> parse_tags_symbol(t_symbol* tags_symbol);
341 std::string string_from_path(const std::vector<std::string>& vs);
342 
346 void register_quarantinized();
347 
348 template <typename T>
349 std::vector<T*> get_objects(typename T::is_model* = nullptr)
350 {
351  return ossia_pd::instance().models.copy();
352 }
353 
354 template <typename T>
355 std::vector<T*> get_objects(typename T::is_device* = nullptr)
356 {
357  return ossia_pd::instance().devices.copy();
358 }
359 
360 template <typename T>
361 std::vector<T*> get_objects(typename T::is_client* = nullptr)
362 {
363  return ossia_pd::instance().clients.copy();
364 }
365 
366 template <typename T>
367 std::vector<T*> get_objects(typename T::is_attribute* = nullptr)
368 {
369  return ossia_pd::instance().attributes.copy();
370 }
371 
372 template <typename T>
373 std::vector<T*> get_objects(typename T::is_parameter* = nullptr)
374 {
375  return ossia_pd::instance().parameters.copy();
376 }
377 
378 template <typename T>
379 std::vector<T*> get_objects(typename T::is_remote* = nullptr)
380 {
381  return ossia_pd::instance().remotes.copy();
382 }
383 
384 template <typename T>
385 std::vector<T*> get_objects(typename T::is_view* = nullptr)
386 {
387  return ossia_pd::instance().views.copy();
388 }
389 
404 template <typename T>
405 T* find_parent(object_base* x, unsigned int start_level, int* level)
406 {
407  if(start_level > x->m_patcher_hierarchy.size())
408  return nullptr; // if we can't reach start level (because we reach the root
409  // canvas before the start_level) then abort
410 
411  std::vector<T*> objects = get_objects<T>();
412 
413  // first remove objects that are deeper in the patcher
414  objects.erase(
415  ossia::remove_if(
416  objects,
417  [&](T* obj) {
418  return obj->m_patcher_hierarchy.size() > x->m_patcher_hierarchy.size();
419  }),
420  objects.end());
421 
422  // then remove the object itself
423  ossia::remove_one(objects, x);
424 
425  // and sort objects by hierarchy size
426  // because the first parent have potentially the same hierarchy depth
427  ossia::sort(objects, [](auto o1, auto o2) {
428  return o1->m_patcher_hierarchy.size() > o2->m_patcher_hierarchy.size();
429  });
430 
431  for(unsigned int i = start_level; i < x->m_patcher_hierarchy.size(); i++)
432  {
433  // remove objects that are deeper than the expected level
434  auto size = x->m_patcher_hierarchy.size() - i;
435  objects.erase(
436  ossia::remove_if(
437  objects, [&](T* obj) { return obj->m_patcher_hierarchy.size() > size; }),
438  objects.end());
439 
440  for(auto o : objects)
441  {
442  if(x->m_patcher_hierarchy[i] == o->m_patcher_hierarchy[0])
443  {
444  (*level) = i;
445  return o;
446  }
447  }
448  }
449  return nullptr;
450 }
451 
456 std::string replace_brackets(const string_view);
457 
466 template <typename T>
467 static inline T* find_parent_alive(object_base* x, int start_level, int* level)
468 {
469  T* obj = find_parent<T>(x, start_level, level);
470  if(obj)
471  {
472  while(obj && obj->m_dead)
473  {
474  obj = find_parent_alive<T>(obj, 1, level);
475  }
476  }
477  assert(!obj || !obj->m_dead);
478  return obj;
479 }
480 
481 #pragma mark template
482 
483 std::string get_absolute_path(object_base* x);
484 
490 const std::vector<t_matcher>& find_parent_node(object_base* x);
491 
500 std::vector<object_base*>
501 find_child_to_register(object_base* x, t_gobj* start_list, t_symbol* classname);
502 
508 bool find_peer(object_base* x);
509 
515 std::vector<ossia::net::node_base*> find_global_nodes(ossia::string_view addr);
516 
523 std::vector<ossia::value> attribute2value(t_atom* atom, long size);
524 
530 ossia::val_type symbol2val_type(t_symbol* s);
531 t_symbol* val_type2symbol(ossia::val_type t);
532 
538 ossia::bounding_mode symbol2bounding_mode(t_symbol* bounding_mode);
539 t_symbol* bounding_mode2symbol(ossia::bounding_mode bm);
540 
541 ossia::access_mode symbol2access_mode(t_symbol* access_mode);
542 t_symbol* access_mode2symbol(ossia::access_mode mode);
543 
550 std::vector<ossia::pd::t_matcher*>
551 make_matchers_vector(object_base* x, const ossia::net::node_base* node);
552 
553 void trig_output_value(ossia::net::node_base* node);
554 
555 #pragma mark Templates
556 
557 template <typename T>
562 auto copy(const T& v)
563 {
564  return v;
565 }
566 
567 template <typename T>
568 // self registering (when creating the object)
569 bool ossia_register(T* x)
570 {
571  if(x->m_dead)
572  return false; // object will be removed soon
573 
574  std::vector<t_matcher> tmp;
575  std::vector<t_matcher>* matchers = &tmp;
576  std::vector<ossia::net::node_base*> nodes;
577 
578  if(x->m_addr_scope == ossia::net::address_scope::global)
579  {
580  std::string addr = x->m_name->s_name;
581  if(x->m_otype == object_class::param || x->m_otype == object_class::model)
582  {
583  size_t pos = 0;
584  while(pos != std::string::npos && nodes.empty())
585  {
586  // remove the last part which should be created
587  pos = addr.find_last_of('/');
588  if(pos < addr.size())
589  {
590  addr = addr.substr(0, pos);
591  }
592  nodes = ossia::pd::find_global_nodes(addr + "/");
593  }
594  addr += '/';
595  }
596  else
597  {
598  nodes = ossia::pd::find_global_nodes(addr);
599  }
600 
601  tmp.reserve(nodes.size());
602  for(auto n : nodes)
603  {
604  tmp.emplace_back(n, (object_base*)nullptr);
605  }
606  }
607  else
608  {
609  std::pair<int, ossia::pd::device*> device{};
610  device.second = find_parent_alive<ossia::pd::device>(x, 0, &device.first);
611 
612  std::pair<int, ossia::pd::client*> client{};
613  client.second = find_parent_alive<ossia::pd::client>(x, 0, &client.first);
614 
615  std::pair<int, ossia::pd::model*> model{};
616  std::pair<int, ossia::pd::view*> view{};
617  int start_level{};
618 
619  if(x->m_otype == object_class::view || x->m_otype == object_class::model)
620  {
621  start_level = 1;
622  }
623 
624  if(x->m_addr_scope == net::address_scope::relative)
625  {
626  // then try to locate a parent view or model
627  if(x->m_otype == object_class::view || x->m_otype == object_class::remote)
628  {
629  view.second = find_parent_alive<ossia::pd::view>(x, start_level, &view.first);
630  }
631 
632  if(!view.second)
633  {
634  model.second = find_parent_alive<ossia::pd::model>(x, 0, &model.first);
635  }
636  }
637 
638  std::vector<std::pair<int, object_base*>> vec{device, client, model, view};
639  // sort pair by ascending order : closest one first
640  std::sort(vec.begin(), vec.end());
641 
642  for(auto& p : vec)
643  {
644  if(p.second)
645  {
646  matchers = &p.second->m_matchers;
647  break;
648  }
649  }
650 
651  if(matchers == &tmp && x->m_addr_scope != ossia::net::address_scope::global)
652  {
653  tmp.emplace_back(
654  &ossia_pd::get_default_device()->get_root_node(), (object_base*)nullptr);
655  }
656  }
657 
658  return x->register_node(*matchers);
659 }
660 
661 template <typename T>
662 void ossia_check_and_register(T* x)
663 {
664  auto& map = ossia_pd::instance().m_root_patcher;
665  auto it = map.find(x->m_patcher_hierarchy.back());
666 
667  // register object only if root patcher have been loadbanged
668  // else the patcher itself will trig a registration on loadbang
669  if(it != map.end() && it->second.is_loadbanged)
670  ossia_register(x);
671 }
672 
673 template <typename T>
674 void address_mess_cb(T* x, t_symbol* address)
675 {
676  // TODO maybe instead use a temporary local char array.
677  std::string name = replace_brackets(address->s_name);
678  x->m_name = gensym(name.c_str());
679  x->m_addr_scope = ossia::net::get_address_scope(x->m_name->s_name);
680  x->update_path();
681  x->unregister();
682  ossia_register(x);
683 }
684 
685 template <typename T>
686 bool obj_is_quarantined(T* x)
687 {
688  return x->quarantine().contains(x);
689 }
690 
691 template <typename T>
692 void obj_quarantining(T* x)
693 {
694  x->quarantine().push_back(x);
695 }
696 
697 template <typename T>
698 void obj_dequarantining(T* x)
699 {
700  x->quarantine().remove_all(x);
701 }
702 
703 } // namespace ossia
The node_base class.
Definition: network/base/node.hpp:48
The value class.
Definition: value.hpp:173
val_type
Enum to represent the types that a value can take.
Definition: parameter_properties.hpp:16
bounding_mode
Address behaviors at crossing domain boundaries.
Definition: parameter_properties.hpp:56
access_mode
Address behaviors at crossing domain boundaries time.
Definition: parameter_properties.hpp:46