SignalDisplay.hpp
1 #pragma once
2 #include <Scenario/Document/Interval/IntervalModel.hpp>
3 
4 #include <Effect/EffectLayer.hpp>
5 #include <Effect/EffectLayout.hpp>
6 
7 #include <ossia/detail/math.hpp>
8 #include <ossia/detail/parse_strict.hpp>
9 #include <ossia/math/safe_math.hpp>
10 #include <ossia/network/value/format_value.hpp>
11 
12 #include <QPainter>
13 
14 #include <halp/audio.hpp>
15 #include <halp/callback.hpp>
16 #include <halp/controls.hpp>
17 #include <halp/meta.hpp>
18 
19 namespace Ui
20 {
21 using value_out = halp::callback<"out", ossia::value>;
22 };
23 namespace Ui::SignalDisplay
24 {
25 struct Node
26 {
27  halp_meta(name, "Signal display")
28  halp_meta(c_name, "SignalDisplay")
29  halp_meta(category, "Monitoring")
30  halp_meta(author, "ossia score")
31  halp_meta(
32  manual_url,
33  "https://ossia.io/score-docs/common-practices/"
34  "4-audio.html#analysing-an-audio-signal")
35  halp_meta(description, "Visualize an input signal")
36  halp_meta(uuid, "9906e563-ddeb-4ecd-908c-952baee2a0a5")
37  halp_flag(fully_custom_item);
38  halp_flag(temporal);
39  halp_flag(loops_by_default);
40 
41  using vec_type = boost::container::small_vector<float, 8>;
42  struct
43  {
44  struct : halp::val_port<"in", std::optional<ossia::value>>
45  {
46  enum widget
47  {
48  control
49  };
50  } port;
51  } inputs;
52 
53  struct
54  {
55  struct : halp::val_port<"out", std::optional<vec_type>>
56  {
57  enum widget
58  {
59  control
60  };
61  } port;
62  } outputs;
63 
65  {
66  vec_type& ret;
67  void operator()() const noexcept { }
68  void operator()(ossia::impulse) const noexcept { ret.push_back(1.f); }
69  void operator()(int v) const noexcept { ret.push_back(v); }
70  void operator()(float v) const noexcept { ret.push_back(v); }
71  void operator()(bool v) const noexcept { ret.push_back(v ? 1.f : 0.f); }
72  void operator()(std::string_view v) const noexcept
73  {
74  if(auto res = ossia::parse_strict<float>(v))
75  ret.push_back(*res);
76  }
77  template <std::size_t N>
78  void operator()(std::array<float, N> arr) const noexcept
79  {
80  ret.insert(ret.end(), arr.begin(), arr.end());
81  }
82  void operator()(const std::vector<ossia::value>& arr) const noexcept
83  {
84  ret.reserve(1 + arr.size());
85  for(auto& val : arr)
86  {
87  ret.push_back(ossia::convert<float>(val));
88  }
89  }
90  void operator()(const ossia::value_map_type& arr) const noexcept
91  {
92  ret.reserve(1 + arr.size());
93  for(auto& [k, v] : arr)
94  {
95  ret.push_back(ossia::convert<float>(v));
96  }
97  }
98  };
99 
100  using tick = halp::tick_flicks;
101  void operator()(halp::tick_flicks tk)
102  {
103  if(auto& opt_v = inputs.port.value)
104  {
105  const auto& v = *opt_v;
106  const float val = ossia::convert<float>(v);
107  if(ossia::safe_isnan(val) || ossia::safe_isinf(val))
108  return;
109 
110  vec_type ret;
111  ret.push_back(float(tk.relative_position));
112 
113  v.apply(value_visitor{ret});
114 
115  outputs.port.value = static_cast<vec_type&&>(ret);
116  }
117  }
119  {
120  static constexpr int timestamp_index = 0;
121  static constexpr int first_value_index = 1;
122 
123  public:
124  Scenario::IntervalModel* m_interval{};
125 
126  std::vector<vec_type> m_values;
127  vec_type min = {0.};
128  vec_type max = {1.};
129 
130  Layer(
131  const Process::ProcessModel& process, const Process::Context& doc,
132  QGraphicsItem* parent)
133  : Process::EffectLayerView{parent}
134  , m_interval{Scenario::closestParentInterval(process.parent())}
135  {
136  if(m_interval)
137  {
138  const Process::PortFactoryList& portFactory
140 
141  auto inl = safe_cast<Process::ControlInlet*>(process.inlets().front());
142 
143  auto fact = portFactory.get(inl->concreteKey());
144  auto port = fact->makePortItem(*inl, doc, this, this);
145  port->setPos(0, 5);
146 
147  connect(
148  m_interval, &Scenario::IntervalModel::executionEvent, this,
149  [this](Scenario::IntervalExecutionEvent ev) {
150  switch(ev)
151  {
152  case Scenario::IntervalExecutionEvent::Playing:
153  case Scenario::IntervalExecutionEvent::Stopped:
154  reset();
155  break;
156  default:
157  break;
158  }
159  });
160 
161  auto outl = safe_cast<Process::ControlOutlet*>(process.outlets().front());
162  connect(
163  outl, &Process::ControlOutlet::valueChanged, this,
164  [this](const ossia::value& v) {
165  auto& val = *v.target<std::vector<ossia::value>>();
166  if(val.size() < 2)
167  return;
168  // FIXME int num_values = val.size() - 1;
169 
170  vec_type vv;
171 
172  for(auto it = val.begin(); it != val.end(); ++it)
173  {
174  vv.push_back(*it->target<float>());
175  }
176 
177  // Handle looping: clear when we jump back in time
178  if(!m_values.empty() && !m_values.back().empty())
179  if(vv[timestamp_index] < m_values.back()[timestamp_index])
180  {
181  m_values.clear();
182  }
183 
184  m_values.push_back(std::move(vv));
185 
186  // FIXMe
187  // if(float_val < min)
188  // min = float_val;
189  // else if(float_val > max)
190  // max = float_val;
191 
192  update();
193  });
194  }
195  }
196 
197  void reset()
198  {
199  min = {0.};
200  max = {1.};
201  m_values.clear();
202  update();
203  }
204 
205  void paint_impl(QPainter* p) const override
206  {
207  if(m_values.size() < 2)
208  return;
209 
210  p->setRenderHint(QPainter::Antialiasing, true);
211  p->setPen(score::Skin::instance().Light.main.pen1_solid_flat_miter);
212 
213  const auto w = width();
214  const auto h = height();
215  double min = 0, max = 1;
216 
217  const auto to_01 = [&](float v) { return 1. - (v - min) / (max - min); };
218  QPointF p0 = {
219  m_values[0][timestamp_index] * w, to_01(m_values[0][first_value_index]) * h};
220  for(std::size_t i = 1; i < m_values.size(); i++)
221  {
222  const auto& value = m_values[i];
223  QPointF p1 = {value[timestamp_index] * w, to_01(value[first_value_index]) * h};
224 
225  if(QLineF l{p0, p1}; l.length() > 2)
226  {
227  p->drawLine(l);
228  p0 = p1;
229  }
230  }
231 
232  p->setRenderHint(QPainter::Antialiasing, false);
233  }
234  };
235 };
236 }
Definition: EffectLayer.hpp:16
Definition: PortFactory.hpp:74
The Process class.
Definition: score-lib-process/Process/Process.hpp:61
Definition: IntervalModel.hpp:50
FactoryType * get(const key_type &k) const noexcept
Get a particular factory from its ConcreteKey.
Definition: InterfaceList.hpp:123
Definition: ProcessContext.hpp:12
Definition: SignalDisplay.hpp:119
Definition: SignalDisplay.hpp:65
Definition: SignalDisplay.hpp:26
const T & interfaces() const
Access to a specific interface list.
Definition: ApplicationContext.hpp:67