2 #include <Scenario/Document/Interval/IntervalModel.hpp>
4 #include <Effect/EffectLayer.hpp>
5 #include <Effect/EffectLayout.hpp>
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>
14 #include <halp/audio.hpp>
15 #include <halp/callback.hpp>
16 #include <halp/controls.hpp>
17 #include <halp/meta.hpp>
21 using value_out = halp::callback<
"out", ossia::value>;
23 namespace Ui::SignalDisplay
27 halp_meta(name,
"Signal display")
28 halp_meta(c_name,
"SignalDisplay")
29 halp_meta(category,
"Monitoring")
30 halp_meta(author,
"ossia score")
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);
39 halp_flag(loops_by_default);
41 using vec_type = boost::container::small_vector<float, 8>;
44 struct : halp::val_port<
"in", std::optional<ossia::value>>
55 struct : halp::val_port<
"out", std::optional<vec_type>>
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
74 if(
auto res = ossia::parse_strict<float>(v))
77 template <std::
size_t N>
78 void operator()(std::array<float, N> arr)
const noexcept
80 ret.insert(ret.end(), arr.begin(), arr.end());
82 void operator()(
const std::vector<ossia::value>& arr)
const noexcept
84 ret.reserve(1 + arr.size());
87 ret.push_back(ossia::convert<float>(val));
90 void operator()(
const ossia::value_map_type& arr)
const noexcept
92 ret.reserve(1 + arr.size());
93 for(
auto& [k, v] : arr)
95 ret.push_back(ossia::convert<float>(v));
100 using tick = halp::tick_flicks;
101 void operator()(halp::tick_flicks tk)
103 if(
auto& opt_v = inputs.port.value)
105 const auto& v = *opt_v;
106 const float val = ossia::convert<float>(v);
107 if(ossia::safe_isnan(val) || ossia::safe_isinf(val))
111 ret.push_back(
float(tk.relative_position));
115 outputs.port.value =
static_cast<vec_type&&
>(ret);
120 static constexpr
int timestamp_index = 0;
121 static constexpr
int first_value_index = 1;
126 std::vector<vec_type> m_values;
133 QGraphicsItem* parent)
135 , m_interval{Scenario::closestParentInterval(process.parent())}
137 setAcceptedMouseButtons({});
143 auto inl = safe_cast<Process::ControlInlet*>(process.inlets().front());
145 auto fact = portFactory.
get(inl->concreteKey());
146 auto port = fact->makePortItem(*inl, doc,
this,
this);
150 m_interval, &Scenario::IntervalModel::executionEvent,
this,
151 [
this](Scenario::IntervalExecutionEvent ev) {
154 case Scenario::IntervalExecutionEvent::Playing:
155 case Scenario::IntervalExecutionEvent::Stopped:
163 auto outl = safe_cast<Process::ControlOutlet*>(process.outlets().front());
165 outl, &Process::ControlOutlet::valueChanged,
this,
166 [
this](
const ossia::value& v) {
167 auto& val = *v.target<std::vector<ossia::value>>();
168 const int N = std::ssize(val);
171 this->num_rows = std::max(this->num_rows, N - 1);
174 min.resize(N, std::numeric_limits<float>::max());
176 max.resize(N, std::numeric_limits<float>::lowest());
179 vv.resize(N, boost::container::default_init);
182 for(
auto it = val.begin(); it != val.end(); ++it)
184 const float r = *it->target<
float>();
195 if(!m_values.empty() && !m_values.back().empty())
196 if(vv[timestamp_index] < m_values.back()[timestamp_index])
202 m_values.push_back(std::move(vv));
218 void draw_row(QPainter* p, qreal w, qreal h,
int row_index,
auto to_01)
const
220 std::optional<int> last_idx;
221 for(
int i = 0; i < std::ssize(m_values) - 1; i++)
223 const auto& v0 = m_values[i];
224 const auto& v1 = m_values[i + 1];
225 if(std::ssize(v0) > row_index)
227 if(last_idx && *last_idx < i)
229 const auto& v0 = m_values[*last_idx];
231 QPointF p0 = {v0[timestamp_index] * w, to_01(v0[row_index]) * h};
232 QPointF p1 = {v1[timestamp_index] * w, to_01(v1[row_index]) * h};
234 if(QLineF l{p0, p1}; l.length() > 2.)
241 if(std::ssize(v1) > row_index)
243 QPointF p0 = {v0[timestamp_index] * w, to_01(v0[row_index]) * h};
244 QPointF p1 = {v1[timestamp_index] * w, to_01(v1[row_index]) * h};
246 if(QLineF l{p0, p1}; l.length() > 2.)
253 const auto& v0 = m_values[*last_idx];
254 QPointF p0 = {v0[timestamp_index] * w, to_01(v0[row_index]) * h};
255 if(QLineF l{p0, p1}; l.length() > 2.)
266 void draw_row_constant(QPainter* p, qreal w, qreal h,
int row_index)
const
268 for(
auto& val : m_values)
270 if(std::ssize(val) > row_index)
272 QPointF p0 = {val[timestamp_index] * w, 0.5 * h};
278 void paint_impl(QPainter* p)
const override
280 if(m_values.size() < 2 || this->num_rows == 0)
284 p->setRenderHint(QPainter::Antialiasing,
true);
285 p->setPen(score::Skin::instance().Light.main.pen1_solid_flat_miter);
287 const auto w = width();
288 const auto h = height() / num_rows;
290 for(
int row = 0; row < num_rows; ++row)
292 const int row_index = row + 1;
294 const auto min = this->min[row_index];
295 const auto max = this->max[row_index];
298 draw_row(p, w, h, row_index, [min, max](
float v) {
299 return 1.f - (v - min) / (max - min);
304 draw_row_constant(p, w, h, row_index);
307 p->translate(QPointF{0, h});
310 p->setRenderHint(QPainter::Antialiasing,
false);
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