Loading...
Searching...
No Matches
ValueLoop.hpp
1#pragma once
2#include <Fx/Looper/PassStack.hpp>
3
4#include <ossia/network/value/value.hpp>
5
6#include <algorithm>
7#include <array>
8#include <cmath>
9#include <cstdint>
10#include <utility>
11
12namespace Nodes::Looper
13{
26{
27public:
28 value_slot() noexcept = default;
29 value_slot(const value_slot& o) { m_value = o.m_value; }
30 value_slot(value_slot&& o) noexcept { m_value = std::move(o.m_value); }
31 value_slot& operator=(const value_slot& o)
32 {
33 if(this != &o)
34 m_value = o.m_value;
35 return *this;
36 }
37 value_slot& operator=(value_slot&& o) noexcept
38 {
39 if(this != &o)
40 m_value = std::move(o.m_value);
41 return *this;
42 }
43 ~value_slot() = default;
44
45 void set(const ossia::value& v) { m_value = v; }
46 void set(ossia::value&& v) noexcept { m_value = std::move(v); }
48 void reset() noexcept { m_value = ossia::value{}; }
49
50 const ossia::value& get() const noexcept { return m_value; }
51 bool empty() const noexcept { return !m_value.valid(); }
52 bool impulse() const noexcept { return m_value.get_type() == ossia::val_type::IMPULSE; }
53
56 static bool same(const ossia::value& a, const ossia::value& b) noexcept
57 {
58 return a.get_type() == b.get_type() && a == b;
59 }
60 bool same(const ossia::value& o) const noexcept { return same(m_value, o); }
61 bool same(const value_slot& o) const noexcept { return same(m_value, o.m_value); }
62
63private:
64 ossia::value m_value;
65};
66
69{
71 int64_t pos;
74 int64_t written;
75 value_slot value;
78 uint32_t serial;
80 uint16_t key;
82 bool impulse;
83 uint8_t pass;
85 uint64_t replaced_by;
86 uint8_t merged;
87};
88
93{
94 int64_t from;
95 int64_t span;
96 uint32_t serial;
97 uint16_t key;
98 uint8_t pass;
99 uint64_t replaced_by;
100 uint8_t merged;
101};
102
116struct value_loop : tagged_pass_stack<value_loop, tagged_pass, 32>
117{
118 static constexpr int max_events = 8192;
119 static constexpr int max_stretches = 512;
120 static constexpr int max_touched = 16;
121
122 std::array<value_event, max_events> events{};
123 std::array<value_stretch, max_stretches> stretches{};
124 int event_count{};
125 int stretch_count{};
126 bool full{};
128 uint32_t next_serial{};
129
133 struct touch
134 {
135 uint16_t key;
136 int64_t first;
137 int64_t at;
138 bool backwards;
139 int stretch;
140 };
141 std::array<touch, max_touched> touches{};
142 int touch_count{};
144 uint32_t serial{};
147 bool whole{};
148
149 // tagged_pass_stack's hooks: the values and the stretches are the
150 // material.
151 void clear_material() noexcept
152 {
153 for(int i = 0; i < event_count; i++)
154 events[i].value.reset();
155 event_count = 0;
156 stretch_count = 0;
157 full = false;
158 next_serial = 0;
159 }
160 void pass_state_begun() noexcept
161 {
162 touch_count = 0;
164 whole = false;
165 }
166
167 void begin_recording() noexcept
168 {
169 tagged_pass_stack::begin_recording();
170 whole = true;
171 }
172 template <typename F>
173 void for_each_material(F&& f) noexcept
174 {
175 for(int i = 0; i < event_count; i++)
176 f(events[i]);
177 for(int i = 0; i < stretch_count; i++)
178 f(stretches[i]);
179 }
180 template <typename F>
181 void remove_material(F&& gone) noexcept
182 {
183 remove_events(gone);
184 stretch_count = int(
185 std::remove_if(stretches.begin(), stretches.begin() + stretch_count, gone)
186 - stretches.begin());
187 for(int i = 0; i < touch_count; i++)
188 touches[i].stretch = find_stretch(touches[i].key);
189 }
190
192 template <typename F>
193 void remove_events(F&& gone) noexcept
194 {
195 int out = 0;
196 for(int i = 0; i < event_count; i++)
197 {
198 if(gone(events[i]))
199 continue;
200 if(out != i)
201 events[out] = std::move(events[i]);
202 out++;
203 }
204 for(int i = out; i < event_count; i++)
205 events[i].value.reset();
206 if(out != event_count)
207 edits++;
208 event_count = out;
209 }
210
213 template <typename T>
214 bool heard(const T& e, bool own_mute = true) const noexcept
215 {
216 return e.pass < count && !replaced(e.replaced_by, e.pass, false)
217 && !(own_mute && layers[e.pass].muted);
218 }
219
221 int find_stretch(uint16_t key) const noexcept
222 {
223 if(!taking || count == 0)
224 return -1;
225 for(int i = stretch_count - 1; i >= 0; i--)
226 if(stretches[i].key == key && stretches[i].serial == serial
227 && stretches[i].pass == count - 1)
228 return i;
229 return -1;
230 }
231
232 const touch* touched(uint16_t key) const noexcept
233 {
234 for(int i = 0; i < touch_count; i++)
235 if(touches[i].key == key)
236 return &touches[i];
237 return nullptr;
238 }
239
242 void touch_key(uint16_t key, int64_t pos, int64_t at, bool backwards) noexcept
243 {
244 if(touched(key) || touch_count >= max_touched || stretch_count >= max_stretches)
245 return;
246 auto& s = stretches[stretch_count];
247 s = value_stretch{
248 .from = pos,
249 .span = 0,
250 .serial = serial,
251 .key = key,
252 .pass = uint8_t(count - 1),
253 .replaced_by = 0,
254 .merged = 0};
255 touches[touch_count++] = touch{key, pos, at, backwards, stretch_count};
256 stretch_count++;
258 }
259
265 void update_open_stretches() noexcept
266 {
267 for(int i = 0; i < touch_count; i++)
268 {
269 const auto& t = touches[i];
270 if(t.stretch < 0)
271 continue;
272 auto& s = stretches[t.stretch];
273 const int64_t span = std::max<int64_t>(0, length - t.at);
274 set_stretch(s, t, pass_written >= length || whole ? length : span);
275 }
276 }
277
278 void set_stretch(value_stretch& s, const touch& t, int64_t span) noexcept
279 {
280 int64_t from = t.backwards ? t.first - span : t.first;
281 if(length > 0)
282 {
283 from %= length;
284 if(from < 0)
285 from += length;
286 }
287 if(s.from != from || s.span != span)
288 edits++;
289 s.from = from;
290 s.span = span;
291 }
292
295 void end_pass() noexcept
296 {
297 if(!taking)
298 return;
299 edits++;
300 for(int i = 0; i < touch_count; i++)
301 {
302 const auto& t = touches[i];
303 if(t.stretch < 0)
304 continue;
305 auto& s = stretches[t.stretch];
306 const int64_t span = whole ? length : pass_written - t.at;
307 set_stretch(s, t, std::min(span, length));
308 }
309 // A stretch the pass did not get to is none.
310 remove_material([](const auto& e) {
311 if constexpr(requires { e.span; })
312 return e.span <= 0;
313 else
314 return false;
315 });
316 touch_count = 0;
317 taking = false;
318 }
319
323 int64_t pos, int64_t written, uint16_t key, const ossia::value& v) noexcept
324 {
325 const bool impulse = v.get_type() == ossia::val_type::IMPULSE;
326 const uint8_t pass = uint8_t(count - 1);
327 for(int i = event_count - 1; i >= 0; i--)
328 {
329 auto& e = events[i];
330 if(e.pass != pass || e.serial != serial)
331 break;
332 if(e.key == key && e.impulse == impulse)
333 {
334 if(e.pos != pos)
335 break;
336 e.value.set(v);
337 edits++;
338 return &e;
339 }
340 }
341 if(event_count >= max_events)
342 {
343 full = true;
344 return nullptr;
345 }
346 edits++;
347 auto& e = events[event_count++];
348 e.pos = pos;
349 e.written = written;
350 e.value.set(v);
351 e.serial = serial;
352 e.key = key;
353 e.impulse = impulse;
354 e.pass = pass;
355 e.replaced_by = 0;
356 e.merged = 0;
357 return &e;
358 }
359
363 void write_over(int64_t lo, int64_t hi, double at) noexcept
364 {
365 if(!taking || count == 0 || touch_count == 0 || at < double(length))
366 return;
367 const uint8_t pass = uint8_t(count - 1);
368 remove_events([&](const value_event& e) {
369 return e.pass == pass && e.serial == serial && !e.impulse && e.pos >= lo
370 && e.pos < hi && touched(e.key)
371 && double(e.written + length) <= at + double(e.pos - lo) + 1e-6;
372 });
373 }
374
376 void assign(const value_loop& o)
377 {
378 for(int i = 0; i < o.event_count; i++)
379 events[i] = o.events[i];
380 for(int i = o.event_count; i < event_count; i++)
381 events[i].value.reset();
382 std::copy_n(o.stretches.begin(), o.stretch_count, stretches.begin());
383 event_count = o.event_count;
384 stretch_count = o.stretch_count;
385 full = o.full;
386 next_serial = o.next_serial;
387 touches = o.touches;
388 touch_count = o.touch_count;
389 serial = o.serial;
390 whole = o.whole;
391 assign_passes(o);
392 }
393
395 static bool holds(const value_stretch& s, int64_t x, int64_t period) noexcept
396 {
397 if(s.span >= period)
398 return true;
399 int64_t d = (x - s.from) % period;
400 if(d < 0)
401 d += period;
402 return d < s.span;
403 }
404
405 int64_t period_for(int pass) const noexcept
406 {
407 return std::clamp<int64_t>(period_of(pass), 1, std::max<int64_t>(1, length));
408 }
409};
410
415{
416 static constexpr int max_refs = 2 * value_loop::max_events;
417 struct ref
418 {
419 int64_t pos;
420 uint32_t serial;
421 int event;
422 };
423 std::array<ref, max_refs> refs{};
424 int count{};
425 std::array<int64_t, max_refs> cuts{};
426 bool own_mute{true};
427
428 static bool in(int pass, int first, int last) noexcept
429 {
430 return first < 0 || (pass >= first && pass <= last);
431 }
432
435 void prepare(const value_loop& loop, uint16_t key, bool own) noexcept
436 {
437 own_mute = own;
438 count = 0;
439 const int64_t len = loop.length;
440 if(len <= 0)
441 return;
442 for(int i = 0; i < loop.event_count; i++)
443 {
444 const auto& e = loop.events[i];
445 if(e.key != key || e.impulse || !loop.heard(e, own_mute))
446 continue;
447 const int64_t period = loop.period_for(e.pass);
448 for(int64_t q = e.pos % period; q < len && count < max_refs; q += period)
449 refs[count++] = {q, e.serial, i};
450 }
451 std::sort(refs.begin(), refs.begin() + count, [](const ref& a, const ref& b) {
452 if(a.serial != b.serial)
453 return a.serial < b.serial;
454 if(a.pos != b.pos)
455 return a.pos < b.pos;
456 return a.event < b.event;
457 });
458 }
459
464 const value_loop& loop, uint16_t key, int64_t x, int first = -1,
465 int last = -1) const noexcept
466 {
467 bool any = false;
468 uint32_t top = 0;
469 for(int s = 0; s < loop.stretch_count; s++)
470 {
471 const auto& st = loop.stretches[s];
472 if(st.key != key || !in(st.pass, first, last) || !loop.heard(st, own_mute)
473 || !value_loop::holds(st, x, loop.period_for(st.pass)))
474 continue;
475 if(!any || st.serial > top)
476 top = st.serial;
477 any = true;
478 }
479 if(!any)
480 return -1;
481 auto lo = std::lower_bound(
482 refs.begin(), refs.begin() + count, top,
483 [](const ref& r, uint32_t s) { return r.serial < s; });
484 auto hi = std::upper_bound(
485 lo, refs.begin() + count, top, [](uint32_t s, const ref& r) { return s < r.serial; });
486 if(lo == hi)
487 return -1;
488 auto it = std::upper_bound(lo, hi, x, [](int64_t x, const ref& r) { return x < r.pos; });
489 return it == lo ? std::prev(hi)->event : std::prev(it)->event;
490 }
491};
492
500{
501 static constexpr int max_points = held_scratch::max_refs;
502 struct point
503 {
504 int64_t pos;
506 int event;
507 };
508 std::array<point, max_points> points{};
509 int count{};
511 uint64_t edits{~uint64_t(0)};
512 int64_t length{-1};
513
516 int at(int64_t x) const noexcept
517 {
518 if(count == 0)
519 return -1;
520 auto it = std::upper_bound(
521 points.begin(), points.begin() + count, x,
522 [](int64_t x, const point& p) { return x < p.pos; });
523 const int i = int(it - points.begin()) - 1;
524 return i >= 0 ? i : count - 1;
525 }
527 int before(int64_t x) const noexcept { return at(x - 1); }
528 int previous(int i) const noexcept { return i > 0 ? i - 1 : count - 1; }
529
533 void update(
534 const value_loop& loop, uint16_t key, held_scratch& scratch, int first = -1,
535 int last = -1, bool own_mute = true, bool force = false) noexcept
536 {
537 if(!force && edits == loop.edits && length == loop.length)
538 return;
539 edits = loop.edits;
540 length = loop.length;
541 count = 0;
542 const int64_t len = loop.length;
543 if(len <= 0)
544 return;
545 scratch.prepare(loop, key, own_mute);
546
547 // Where the held value may change: on a value, and where a stretch
548 // starts or ends.
549 auto& cuts = scratch.cuts;
550 int c = 0;
551 for(int i = 0; i < scratch.count && c < held_scratch::max_refs; i++)
552 if(held_scratch::in(loop.events[scratch.refs[i].event].pass, first, last))
553 cuts[c++] = scratch.refs[i].pos;
554 for(int s = 0; s < loop.stretch_count; s++)
555 {
556 const auto& st = loop.stretches[s];
557 if(st.key != key || !held_scratch::in(st.pass, first, last)
558 || !loop.heard(st, own_mute))
559 continue;
560 const int64_t period = loop.period_for(st.pass);
561 if(st.span >= period)
562 continue;
563 for(int64_t base = 0; base < len && c + 2 <= held_scratch::max_refs; base += period)
564 for(const int64_t q : {base + st.from % period, base + (st.from + st.span) % period})
565 if(q < len)
566 cuts[c++] = q;
567 }
568 if(c == 0)
569 return;
570 if(c < held_scratch::max_refs)
571 cuts[c++] = 0;
572 std::sort(cuts.begin(), cuts.begin() + c);
573 c = int(std::unique(cuts.begin(), cuts.begin() + c) - cuts.begin());
574
575 for(int i = 0; i < c; i++)
576 {
577 const int e = scratch.value_at(loop, key, cuts[i], first, last);
578 if(count > 0)
579 {
580 const int prev = points[count - 1].event;
581 if(prev == e
582 || (prev >= 0 && e >= 0 && loop.events[prev].value.same(loop.events[e].value)))
583 continue;
584 }
585 points[count++] = {cuts[i], e};
586 }
587 }
588};
589}
Definition ValueLoop.hpp:26
void reset() noexcept
Lets go of what it holds.
Definition ValueLoop.hpp:48
static bool same(const ossia::value &a, const ossia::value &b) noexcept
Definition ValueLoop.hpp:56
Definition AudioLayers.hpp:14
Definition ValueLoop.hpp:418
Definition ValueLoop.hpp:415
int value_at(const value_loop &loop, uint16_t key, int64_t x, int first=-1, int last=-1) const noexcept
Definition ValueLoop.hpp:463
void prepare(const value_loop &loop, uint16_t key, bool own) noexcept
Definition ValueLoop.hpp:435
Definition ValueLoop.hpp:503
int event
The value of the loop that holds from there, -1 for none.
Definition ValueLoop.hpp:506
Definition ValueLoop.hpp:500
void update(const value_loop &loop, uint16_t key, held_scratch &scratch, int first=-1, int last=-1, bool own_mute=true, bool force=false) noexcept
Definition ValueLoop.hpp:533
int before(int64_t x) const noexcept
The point that holds just before x.
Definition ValueLoop.hpp:527
int at(int64_t x) const noexcept
Definition ValueLoop.hpp:516
uint64_t edits
What it was worked out from.
Definition ValueLoop.hpp:511
int64_t length
Frames in the loop.
Definition PassStack.hpp:79
int count
Passes heard.
Definition PassStack.hpp:75
std::array< Pass, max_passes+1 > layers
The passes heard, then the ones redo brings back.
Definition PassStack.hpp:73
bool replaced(uint64_t by, int self_pass, bool taking) const noexcept
Definition PassStack.hpp:383
Definition PassStack.hpp:549
void assign_passes(const tagged_pass_stack &o) noexcept
Definition PassStack.hpp:718
uint64_t edits
Changes each time what is heard of the loop may have changed.
Definition PassStack.hpp:556
A value of the loop.
Definition ValueLoop.hpp:69
bool impulse
Fired where it is, rather than held until the next one.
Definition ValueLoop.hpp:82
uint64_t replaced_by
The passes that replace it, a bit each.
Definition ValueLoop.hpp:85
int64_t pos
Loop position, in frames.
Definition ValueLoop.hpp:71
uint32_t serial
Definition ValueLoop.hpp:78
int64_t written
Definition ValueLoop.hpp:74
uint16_t key
What the value is of: one value stream for now, so always 0.
Definition ValueLoop.hpp:80
Definition ValueLoop.hpp:134
Definition ValueLoop.hpp:117
void remove_events(F &&gone) noexcept
The values gone says are let go of.
Definition ValueLoop.hpp:193
uint32_t serial
The serial of the pass being taken in.
Definition ValueLoop.hpp:144
value_event * add_value(int64_t pos, int64_t written, uint16_t key, const ossia::value &v) noexcept
Definition ValueLoop.hpp:322
void assign(const value_loop &o)
Copies what another loop holds, without its unused storage.
Definition ValueLoop.hpp:376
void update_open_stretches() noexcept
Definition ValueLoop.hpp:265
bool whole
Definition ValueLoop.hpp:147
void end_pass() noexcept
Definition ValueLoop.hpp:295
uint32_t next_serial
Numbers the passes in the order they are made.
Definition ValueLoop.hpp:128
static bool holds(const value_stretch &s, int64_t x, int64_t period) noexcept
Whether a stretch holds position x, its pass repeating every period.
Definition ValueLoop.hpp:395
bool heard(const T &e, bool own_mute=true) const noexcept
Definition ValueLoop.hpp:214
void touch_key(uint16_t key, int64_t pos, int64_t at, bool backwards) noexcept
Definition ValueLoop.hpp:242
int find_stretch(uint16_t key) const noexcept
The open stretch the pass being taken in holds key over, -1 for none.
Definition ValueLoop.hpp:221
void write_over(int64_t lo, int64_t hi, double at) noexcept
Definition ValueLoop.hpp:363
Definition ValueLoop.hpp:93