Loading...
Searching...
No Matches
PassStack.hpp
1#pragma once
2#include <Fx/Looper/Transport.hpp>
3
4#include <algorithm>
5#include <array>
6#include <bit>
7#include <cmath>
8#include <cstdint>
9#include <utility>
10
11namespace Nodes::Looper
12{
15{
18 int64_t period{};
20 int64_t loop_before{};
21 int64_t loop_after{};
24 bool muted{};
25};
26
64template <typename Store, typename Pass, int MaxLayers>
66{
68 static constexpr int max_layers = MaxLayers;
70 static constexpr int max_passes = max_layers + 1;
71
73 std::array<Pass, max_passes + 1> layers{};
75 int count{};
77 int total{};
79 int64_t length{};
82 uint64_t pushes{};
83
88 {
89 bool valid{};
90 int at{};
91 Pass a{};
92 Pass b{};
93 } unmerge;
94
99 {
100 bool valid{};
103 bool returned{};
104 int at{};
106 int by{-1};
107 Pass layer{};
108 } aside;
109
110 merge_log merged_passes;
111
112 bool empty() const noexcept { return count == 0 || length == 0; }
113
114 Pass* top() noexcept { return count > 0 ? &layers[count - 1] : nullptr; }
115
117 static int64_t period_in(const Pass& l, int64_t loop_length) noexcept
118 {
119 return l.period > 0 ? l.period : loop_length;
120 }
121 int64_t period_of(const Pass& l) const noexcept { return period_in(l, length); }
122 int64_t period_of(int pass) const noexcept { return period_in(layers[pass], length); }
123
125 int64_t merged_period(const Pass& a, const Pass& b) const noexcept
126 {
127 const int64_t p = std::max(period_of(a), period_of(b));
128 return p == length ? 0 : p;
129 }
130
135 bool extend(int64_t unit, int64_t max_length, bool top_open = true) noexcept
136 {
137 if(count == 0 || unit <= 0 || length + unit > max_length)
138 return false;
139 for(int i = 0; i < count - (top_open ? 1 : 0); i++)
140 if(layers[i].period == 0)
141 layers[i].period = length;
142 length += unit;
143 return true;
144 }
145
147 bool set_muted(int k, bool m) noexcept
148 {
149 if(k < 0 || k >= count || layers[k].muted == m)
150 return false;
151 self().muting(k);
152 layers[k].muted = m;
153 drop_unmerge();
154 self().passes_changed();
155 return true;
156 }
157
158 void clear() noexcept
159 {
160 unmerge.valid = false;
161 aside.valid = false;
162 count = 0;
163 total = 0;
164 length = 0;
165 self().cleared();
166 self().passes_changed();
167 }
168
170 Pass& begin_first_pass() noexcept
171 {
172 clear();
173 count = 1;
174 total = 1;
175 layers[0] = Pass{};
176 return layers[0];
177 }
178
180 Pass& push_pass() noexcept
181 {
182 auto& l = layers[count];
183 l = Pass{};
184 l.loop_before = length;
185 l.loop_after = length;
186 count++;
187 total = count;
188 pushes++;
189 aside.by = count - 1;
190 self().passes_changed();
191 return l;
192 }
193
196 void drop_redo() noexcept
197 {
198 const bool keep_aside = aside.valid && !aside.returned;
199 self().redo_dropped(keep_aside);
200 total = count;
201 if(!keep_aside)
202 aside.valid = false;
203 drop_unmerge();
204 self().passes_changed();
205 }
206
208 void forget_aside() noexcept
209 {
210 if(!aside.valid)
211 return;
212 aside.valid = false;
213 if(!aside.returned)
214 self().aside_forgotten();
215 }
216
218 void drop_unmerge() noexcept
219 {
220 if(std::exchange(unmerge.valid, false))
221 self().unmerge_dropped();
222 }
223
226 void set_aside(int k) noexcept
227 {
228 if(k < 0 || k >= count || count < 2 || !self().may_remove(k))
229 return;
230 forget_aside();
231 drop_unmerge();
232 move_aside(k);
233 aside.by = -1;
234 }
235
237 int returns_on_undo() const noexcept
238 {
239 return aside.valid && !aside.returned && count > 0 && aside.by == count - 1 ? aside.at
240 : -1;
241 }
242
245 int leaves_on_redo() const noexcept
246 {
247 return aside.valid && aside.returned && count + 1 == total && aside.at < count ? aside.at
248 : -1;
249 }
250
253 void put_back() noexcept
254 {
255 const int k = aside.at;
256 if(total >= max_passes)
257 {
258 forget_aside();
259 return;
260 }
261 self().aside_returned(k);
262 for(int i = total; i > k; i--)
263 layers[i] = layers[i - 1];
264 layers[k] = aside.layer;
265 count++;
266 total++;
267 aside.returned = true;
268 self().passes_changed();
269 }
270
271 bool undo() noexcept
272 {
273 if(count == 0)
274 return false;
275 self().undoing();
276 // The pass merged into the newest one comes out of it.
277 if(unmerge.valid && unmerge.at == count - 1)
278 {
279 forget_aside();
280 const int at = unmerge.at;
281 unmerge.valid = false;
282 self().unmerged(at);
283 layers[at] = unmerge.a;
284 layers[at + 1] = unmerge.b;
285 layers[at + 1].loop_after = length;
286 length = unmerge.b.loop_before;
287 total = count + 1;
288 self().passes_changed();
289 return true;
290 }
291 drop_unmerge();
292 const bool back = returns_on_undo() >= 0;
293 auto& l = layers[count - 1];
294 if(count > 1)
295 {
296 l.loop_after = length;
297 length = l.loop_before;
298 }
299 count--;
300 if(back)
301 put_back();
302 self().passes_changed();
303 return true;
304 }
305
306 bool redo() noexcept
307 {
308 if(count >= total)
309 return false;
310 self().undoing();
311 drop_unmerge();
312 // The pass that replaced a layer takes its place again.
313 if(const int k = leaves_on_redo(); k >= 0)
314 {
315 const int by = aside.by;
316 move_aside(k);
317 aside.by = by;
318 }
319 if(count > 0)
320 length = layers[count].loop_after;
321 count++;
322 self().passes_changed();
323 return true;
324 }
325
329 bool erase(int k) noexcept
330 {
331 if(k < 0 || k >= count || count < 2 || !self().may_remove(k))
332 return false;
333 drop_unmerge();
334 forget_aside();
335 self().removing(k);
336 self().erased(k);
337 shift_down(k);
338 self().passes_changed();
339 return true;
340 }
341
343 void begin_merge() noexcept
344 {
345 if(total > count)
346 drop_redo();
347 drop_unmerge();
348 forget_aside();
349 }
350
354 void merged(int at, const Pass& merged, bool undoable, const Pass& a, const Pass& b) noexcept
355 {
356 if(undoable)
357 unmerge = unmerge_state{.valid = true, .at = at, .a = a, .b = b};
358 layers[at] = merged;
359 for(int i = at + 1; i + 1 < count; i++)
360 layers[i] = layers[i + 1];
361 count--;
362 total = count;
363 merged_passes.add(at);
364 self().passes_changed();
365 }
366
370 Pass merged_pass(const Pass& a, const Pass& b) const noexcept
371 {
372 Pass m = a;
373 m.period = merged_period(a, b);
374 m.loop_after = b.loop_after;
375 m.muted = a.muted && b.muted;
376 return m;
377 }
378
383 bool replaced(uint64_t by, int self_pass, bool taking) const noexcept
384 {
385 for(uint64_t m = by; m != 0; m &= m - 1)
386 {
387 const int q = std::countr_zero(m);
388 if(q == self_pass)
389 return true;
390 if(q < count && !layers[q].muted && !(taking && q == count - 1))
391 return true;
392 }
393 return false;
394 }
395
396private:
397 Store& self() noexcept { return static_cast<Store&>(*this); }
398
399 void shift_down(int k) noexcept
400 {
401 for(int i = k; i + 1 < total; i++)
402 layers[i] = layers[i + 1];
403 count--;
404 total--;
405 }
406
407 void move_aside(int k) noexcept
408 {
409 self().removing(k);
410 aside.valid = true;
411 aside.returned = false;
412 aside.at = k;
413 aside.layer = layers[k];
414 self().aside_taken(k);
415 shift_down(k);
416 self().passes_changed();
417 }
418};
419
422inline constexpr uint64_t without_pass(uint64_t m, int k) noexcept
423{
424 const uint64_t low = m & ((uint64_t(1) << k) - 1);
425 return low | ((m >> (k + 1)) << k);
426}
427
430inline constexpr uint64_t with_room_at(uint64_t m, int k) noexcept
431{
432 const uint64_t low = m & ((uint64_t(1) << k) - 1);
433 return low | ((m >> k) << (k + 1));
434}
435
444
454template <int MaxPasses>
456{
457 static constexpr int aside_pass = MaxPasses;
458 static constexpr int lost_pass = MaxPasses + 1;
459 static_assert(lost_pass < 64);
460 static constexpr uint64_t bit(int k) noexcept { return uint64_t(1) << k; }
461
462 template <typename T>
463 static void erase(T& e, int k) noexcept
464 {
465 e.replaced_by = without_pass(e.replaced_by, k);
466 if(e.pass > k)
467 e.pass--;
468 }
469
470 template <typename T>
471 static void set_aside(T& e, int k) noexcept
472 {
473 const bool by_k = e.replaced_by & bit(k);
474 e.replaced_by = without_pass(e.replaced_by, k);
475 if(by_k)
476 e.replaced_by |= bit(aside_pass);
477 if(e.pass == k)
478 e.pass = uint8_t(aside_pass);
479 else if(e.pass > k)
480 e.pass--;
481 }
482
483 template <typename T>
484 static void put_back(T& e, int k) noexcept
485 {
486 const bool by_aside = e.replaced_by & bit(aside_pass);
487 e.replaced_by = with_room_at(e.replaced_by & ~bit(aside_pass), k);
488 if(by_aside)
489 e.replaced_by |= bit(k);
490 if(e.pass == aside_pass)
491 e.pass = uint8_t(k);
492 else if(e.pass >= k)
493 e.pass++;
494 }
495
500 template <typename T>
501 static void merge(T& e, int a, bool lost_a, bool lost_b) noexcept
502 {
503 const int b = a + 1;
504 const bool had_a = e.replaced_by & bit(a);
505 const bool had_b = e.replaced_by & bit(b);
506 e.merged = uint8_t(
507 (e.pass == b ? from_upper : 0) | (had_a ? replaced_by_lower : 0)
508 | (had_b ? replaced_by_upper : 0));
509 uint64_t r = e.replaced_by & ~(bit(a) | bit(b));
510 if((had_a && !lost_a) || (had_b && !lost_b))
511 r |= bit(a);
512 e.replaced_by = without_pass(r, b);
513 if((e.pass == a && lost_a) || (e.pass == b && lost_b))
514 e.pass = uint8_t(lost_pass);
515 else if(e.pass >= b && e.pass < aside_pass)
516 e.pass--;
517 }
518
520 template <typename T>
521 static void unmerge(T& e, int a) noexcept
522 {
523 const int b = a + 1;
524 uint64_t r = with_room_at(e.replaced_by, b) & ~bit(a);
525 if(e.merged & replaced_by_lower)
526 r |= bit(a);
527 if(e.merged & replaced_by_upper)
528 r |= bit(b);
529 e.replaced_by = r;
530 if(e.pass == lost_pass || (e.merged & from_upper))
531 e.pass = uint8_t((e.merged & from_upper) ? b : a);
532 e.merged = 0;
533 }
534};
535
547template <typename Store, typename Pass, int MaxLayers>
548struct tagged_pass_stack : pass_stack<Store, Pass, MaxLayers>
549{
553 static constexpr int aside_pass = tags::aside_pass;
554
556 uint64_t edits{};
557
558 // The pass being taken in: where it went.
559 int64_t pass_start{};
560 int64_t pass_written{};
567 bool taking{};
568
569 // pass_stack's hooks: the material moves with its passes.
570 void passes_changed() noexcept { edits++; }
571 bool may_remove(int k) const noexcept { return !(taking && k == this->count - 1); }
572 void muting(int) noexcept { }
573 void removing(int) noexcept { }
574 void erased(int k) noexcept
575 {
576 store().remove_material([k](const auto& e) { return e.pass == k; });
577 store().for_each_material([k](auto& e) { tags::erase(e, k); });
578 }
579 void aside_taken(int k) noexcept
580 {
581 store().for_each_material([k](auto& e) { tags::set_aside(e, k); });
582 }
583 void aside_returned(int k) noexcept
584 {
585 store().for_each_material([k](auto& e) { tags::put_back(e, k); });
586 }
587 void aside_forgotten() noexcept
588 {
589 store().remove_material([](const auto& e) { return e.pass == aside_pass; });
590 store().for_each_material([](auto& e) { e.replaced_by &= ~tags::bit(aside_pass); });
591 }
592 void redo_dropped(bool keep_aside) noexcept
593 {
594 const int heard_passes = this->count;
595 store().remove_material([heard_passes, keep_aside](const auto& e) {
596 return e.pass >= heard_passes && !(keep_aside && e.pass == aside_pass);
597 });
598 uint64_t kept
599 = this->count >= 64 ? ~uint64_t(0) : (uint64_t(1) << this->count) - 1;
600 if(keep_aside)
601 kept |= tags::bit(aside_pass);
602 store().for_each_material([kept](auto& e) { e.replaced_by &= kept; });
603 }
604 void unmerge_dropped() noexcept
605 {
606 store().remove_material([](const auto& e) { return e.pass == tags::lost_pass; });
607 }
608 void unmerged(int at) noexcept
609 {
610 store().for_each_material([at](auto& e) { tags::unmerge(e, at); });
611 }
612 void cleared() noexcept
613 {
614 taking = false;
615 store().clear_material();
616 }
617 void undoing() noexcept { }
618
620 bool in_pass(int64_t k) const noexcept
621 {
622 if(!taking)
623 return false;
624 if(pass_written >= this->length)
625 return true;
626 int64_t d = k - pass_start;
627 if(d < 0)
628 d += this->length;
629 return d < pass_written;
630 }
631
634 bool replacing() const noexcept
635 {
636 return taking && this->count > 0 && !this->layers[this->count - 1].muted;
637 }
638
639 void begin_recording() noexcept
640 {
641 this->begin_first_pass();
642 begin_pass_state(0);
643 }
644
650 int64_t start, int max = stack::max_layers, bool oldest = true,
651 int replace = -1) noexcept
652 {
653 this->drop_redo();
654 this->forget_aside();
655 if(replace >= 0)
656 this->set_aside(replace);
657 max = std::clamp(max, 2, stack::max_layers);
658 if(oldest)
659 {
660 while(this->count >= max)
661 merge(0);
662 }
663 else
664 {
665 // The pass before it was not merged yet.
666 while(this->count > max)
667 merge(this->count - 2);
668 }
669 this->push_pass();
670 begin_pass_state(start);
671 }
672
677 void merge(int at, bool undoable = false) noexcept
678 {
679 const int a = at, b = at + 1;
680 if(a < 0 || b >= this->count)
681 return;
682 this->begin_merge();
683 const Pass was_a = this->layers[a], was_b = this->layers[b];
684 const bool lost_a = was_a.muted && !was_b.muted;
685 const bool lost_b = was_b.muted && !was_a.muted;
686 store().for_each_material([=](auto& e) { tags::merge(e, a, lost_a, lost_b); });
687 this->merged(a, this->merged_pass(was_a, was_b), undoable, was_a, was_b);
688 }
689
690 void begin_pass_state(int64_t start) noexcept
691 {
692 pass_start = start;
693 pass_written = 0;
694 pass_progress = 0.;
695 pass_backwards = false;
696 taking = true;
697 store().pass_state_begun();
698 }
699
702 void advance_pass(double frames, bool backwards = false) noexcept
703 {
704 pass_progress += frames;
705 const auto written = int64_t(std::floor(pass_progress + 1e-6));
706 if(backwards && this->length > 0)
707 {
708 pass_start = (pass_start - (written - pass_written)) % this->length;
709 if(pass_start < 0)
710 pass_start += this->length;
711 }
712 pass_backwards = backwards;
713 pass_written = written;
714 }
715
718 void assign_passes(const tagged_pass_stack& o) noexcept
719 {
720 this->count = o.count;
721 this->total = o.total;
722 this->length = o.length;
723 this->layers = o.layers;
724 this->unmerge = o.unmerge;
725 this->aside = o.aside;
726 edits = o.edits;
727 pass_start = o.pass_start;
728 pass_written = o.pass_written;
729 pass_progress = o.pass_progress;
730 pass_backwards = o.pass_backwards;
731 taking = o.taking;
732 }
733
736 int64_t distance(int64_t from, int64_t to) const noexcept
737 {
738 if(this->length <= 0)
739 return std::max<int64_t>(1, to - from);
740 int64_t d = (to - from) % this->length;
741 if(d <= 0)
742 d += this->length;
743 return d;
744 }
745
746private:
747 Store& store() noexcept { return static_cast<Store&>(*this); }
748};
749}
Definition AudioLayers.hpp:14
merge_mark
Definition PassStack.hpp:439
@ replaced_by_upper
The upper pass replaced it.
Definition PassStack.hpp:442
@ from_upper
It is of the upper pass.
Definition PassStack.hpp:440
@ replaced_by_lower
The lower pass replaced it.
Definition PassStack.hpp:441
constexpr uint64_t with_room_at(uint64_t m, int k) noexcept
Definition PassStack.hpp:430
constexpr uint64_t without_pass(uint64_t m, int k) noexcept
Definition PassStack.hpp:422
Definition PassStack.hpp:99
bool returned
Definition PassStack.hpp:103
int by
The pass that replaced it.
Definition PassStack.hpp:106
Definition PassStack.hpp:88
Definition PassStack.hpp:66
bool set_muted(int k, bool m) noexcept
Mutes or unmutes pass k, k being heard.
Definition PassStack.hpp:147
int total
Passes kept, the ones past count being what redo brings back.
Definition PassStack.hpp:77
static constexpr int max_layers
Most layers kept: Max layers is at most this.
Definition PassStack.hpp:68
void drop_unmerge() noexcept
Undo is not to take the last merge apart: what it kept for that goes.
Definition PassStack.hpp:218
void forget_aside() noexcept
Forgets the layer set aside.
Definition PassStack.hpp:208
int64_t length
Frames in the loop.
Definition PassStack.hpp:79
Pass & push_pass() noexcept
Adds a pass over the ones heard, room having been made for it.
Definition PassStack.hpp:180
int64_t merged_period(const Pass &a, const Pass &b) const noexcept
The period two passes merged into one repeat with.
Definition PassStack.hpp:125
int count
Passes heard.
Definition PassStack.hpp:75
int leaves_on_redo() const noexcept
Definition PassStack.hpp:245
void merged(int at, const Pass &merged, bool undoable, const Pass &a, const Pass &b) noexcept
Definition PassStack.hpp:354
bool extend(int64_t unit, int64_t max_length, bool top_open=true) noexcept
Definition PassStack.hpp:135
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
bool erase(int k) noexcept
Definition PassStack.hpp:329
static int64_t period_in(const Pass &l, int64_t loop_length) noexcept
The length a pass repeats with in a loop of loop_length frames.
Definition PassStack.hpp:117
Pass merged_pass(const Pass &a, const Pass &b) const noexcept
Definition PassStack.hpp:370
int returns_on_undo() const noexcept
Undoing the top pass puts back the layer it replaced: where it goes.
Definition PassStack.hpp:237
void begin_merge() noexcept
A merge is about to be worked out: what it would make stale goes.
Definition PassStack.hpp:343
void drop_redo() noexcept
Definition PassStack.hpp:196
uint64_t pushes
Definition PassStack.hpp:82
Pass & begin_first_pass() noexcept
Forgets every pass for a recording: its pass, the first one.
Definition PassStack.hpp:170
void put_back() noexcept
Definition PassStack.hpp:253
void set_aside(int k) noexcept
Definition PassStack.hpp:226
static constexpr int max_passes
A pass past Max layers is one more until it is merged.
Definition PassStack.hpp:70
Definition PassStack.hpp:456
static void merge(T &e, int a, bool lost_a, bool lost_b) noexcept
Definition PassStack.hpp:501
static void unmerge(T &e, int a) noexcept
The merge of a and a + 1 is undone.
Definition PassStack.hpp:521
Definition PassStack.hpp:549
void merge(int at, bool undoable=false) noexcept
Definition PassStack.hpp:677
void assign_passes(const tagged_pass_stack &o) noexcept
Definition PassStack.hpp:718
void advance_pass(double frames, bool backwards=false) noexcept
Definition PassStack.hpp:702
void begin_overdub(int64_t start, int max=stack::max_layers, bool oldest=true, int replace=-1) noexcept
Definition PassStack.hpp:649
uint64_t edits
Changes each time what is heard of the loop may have changed.
Definition PassStack.hpp:556
bool pass_backwards
Definition PassStack.hpp:566
double pass_progress
Definition PassStack.hpp:563
static constexpr int aside_pass
Where the material of a pass set aside is, past every pass there can be.
Definition PassStack.hpp:553
int64_t distance(int64_t from, int64_t to) const noexcept
Definition PassStack.hpp:736
bool replacing() const noexcept
Definition PassStack.hpp:634
bool in_pass(int64_t k) const noexcept
Position k is in what the pass being taken in went over.
Definition PassStack.hpp:620
What a loop whose material is kept apart keeps of a pass besides it.
Definition PassStack.hpp:15
bool muted
Definition PassStack.hpp:24
int64_t period
Definition PassStack.hpp:18
int64_t loop_before
Length of the loop before and after the pass, for undo and redo.
Definition PassStack.hpp:20