2#include <Fx/Looper/LooperNode.hpp>
3#include <Fx/Looper/ValueLoop.hpp>
4#include <Fx/Looper/ValueTake.hpp>
7#include <ossia/dataflow/exec_state_facade.hpp>
8#include <ossia/dataflow/token_request.hpp>
9#include <ossia/dataflow/value_port.hpp>
11#include <halp/controls.hpp>
12#include <halp/layout.hpp>
13#include <halp/meta.hpp>
19namespace Nodes::ValueLooper
23 halp_meta(name,
"Looper (values)")
24 halp_meta(c_name,
"value_looper")
25 halp_meta(category,
"Control/Mappings")
26 halp_meta(author,
"ossia score")
27 halp_meta(manual_url,
"https://ossia.io/score-docs/processes/value-looper.html")
30 "Loop a stream of values in passes: numbers and vectors held as a "
31 "curve, impulses fired where they came, and turn the loop into "
32 "automations of the timeline")
33 halp_meta(uuid,
"399796c5-9e86-4a00-8671-3e759ad6fb33");
34 halp_meta(recommended_height, 200);
35 halp_flag(process_exec);
37 using LoopMode = Looper::LoopMode;
50 struct : halp::enum_t<LoopMode,
"Mode">
52 using halp::enum_t<LoopMode,
"Mode">::operator=;
55 "What the looper is doing, or about to do on the grid: playing the "
56 "loop, recording it, overdubbing on top of it, or nothing. It follows "
57 "every change, by an action or a recording's end. Record on a loop "
58 "overdubs it: Clear it first to record a new one. Overdub on an "
59 "empty loop records it, then overdubs.")
61 std::function<void(std::string_view)> update_controller;
63 void update(
Node& n)
noexcept { n.loop_arrived(); }
70 using enum_combobox::operator=;
73 "Whether the values that come in go out as well as the loop: never, "
74 "only while recording or overdubbing, or always. Always, a value "
75 "that comes in wins over the loop while the input moves; once it "
76 "stops, the loop takes over again from the value it holds there.")
81 struct : halp::spinbox_i32<
"Record bars", halp::irange{0, 64, 4}>
85 "How long a recording runs before After record takes over. Zero "
86 "leaves it to be ended by hand.")
91 struct : halp::impulse_button<
"Undo">
93 halp_meta(description,
"Undoes the last pass, erase or mute")
95 struct : halp::impulse_button<
"Redo">
97 halp_meta(description,
"Redoes what Undo took back")
99 struct : halp::impulse_button<
"Clear">
101 halp_meta(description,
"Empties the loop and forgets every pass")
104 struct : halp::spinbox_i32<
"Late stop (ms)", halp::irange{0, 500, 120}>
108 "A recording asked to stop this long after a point of the grid ends "
112 struct : halp::spinbox_i32<
"Capture bars", halp::irange{1, 64, 4}>
116 "How many bars Capture makes a loop of when there is none. Over a "
117 "loop, Capture takes one round of it, whatever this says.")
120 struct : halp::impulse_button<
"Capture">
124 "Takes the values that came in lately, recorded or not, without having "
125 "to press Record first. With no loop, the last Capture bars become "
126 "the loop, starting from the value held before them, played at once. "
127 "Over a loop, the values of its last round become a new layer, where "
128 "they came in the loop. Undo takes it out. Pressed just after a grid "
129 "point, it ends there, as Late says.")
134 using enum_combobox::operator=;
137 "What Send, or a drag from it, places: what is heard of the loop "
138 "(Mix), each pass in a file of its own (Layers), or both, a take of "
139 "numbers, vectors or lists of numbers as an automation for each "
140 "number, a box for each file. A take written as a pass closes has "
141 "these files; one Send or a drag asks for, or the score's stop "
142 "leaves, has them all, so that Export can still be changed once the "
143 "score is stopped. Dragging a lane places its layer alone. A take "
144 "with impulses, text or maps is kept as a file only.")
149 using enum_combobox::operator=;
152 "What an overdub does at the end of the loop: go round it again "
153 "(Wrap), or make the loop longer by its own length each time, the "
154 "passes under it playing again (Extend). An extended loop ends when "
155 "overdubbing stops.")
158 struct : halp::toggle<
"Follow tempo", halp::default_on_toggle>
162 "Plays the loop at the tempo of the score: it keeps to the bars when "
163 "the tempo changes. Off, it keeps the speed it was recorded at.")
168 struct : halp::spinbox_i32<
"Layer", halp::irange{1, 32, 1}>
172 "The layer Mute layer, Erase layer and Solo layer act on, counting "
173 "from the oldest one: the lane of that number from the top while "
174 "there are no more layers than lanes, and the file of that number of "
175 "a take written as layers. It moves to each new layer as it is "
177 std::function<void(
int)> update_controller;
180 struct : halp::toggle<
"Reverse">
184 "Plays the loop backwards, from where the playhead is: a value goes "
185 "back to the one it had before, impulses fire where they were. An "
186 "overdub records where the playhead goes, as on tape: what is played "
187 "over the reversed loop plays reversed once it goes forwards again.")
188 std::function<void(
bool)> update_controller;
195 "Plays the loop slower or faster. An overdub records where the "
196 "playhead goes: what is played at x1/2 plays twice as fast at x1. "
197 "The grid, the bars and the loop's length stay in the score's time; "
198 "combines with Follow tempo.")
199 std::function<void(
float)> update_controller;
204 using enum_combobox::operator=;
207 std::string_view values[2] = {
"Whole",
"Per cycle"};
212 "What an overdub that goes round the loop several times leaves: one "
213 "layer (Whole), each round writing over the one before, or a layer "
214 "for each time round (Per cycle), cut on the loop's start, to keep "
215 "the best one with Solo layer or Mute layer, erase the others, or "
216 "splice them in the timeline from a take written as layers. With "
217 "Past the end on Extend, a cut is made each time the loop grows.")
246 "Layers kept apart, for undo, mute, solo and erase. What a pass does "
247 "past it is what At max layers says.")
254 struct : halp::val_port<
"Phase",
float>
256 halp_meta(description,
"Position in the loop, from 0 to 1")
267 const bool ok = job.run();
268 return [slot = job.slot, number = job.number, alone = job.alone, layer = job.layer,
269 placeable = job.components > 0, nothing = job.nothing, ok](
Node& self) {
272 const bool first = alone || layer == self.m_export_first;
274 self.m_written_placeable = placeable;
276 self.m_written_placeable = self.m_written_placeable && placeable;
277 self.take_written(slot, number, ok, alone);
282 std::function<void(Looper::view_message)> send_message;
291 ossia::exec_state_facade ossia_state;
294 std::shared_ptr<Looper::take_writer> writer;
295 std::shared_ptr<Looper::value_export> export_data;
302 export_data = std::make_shared<Looper::value_export>();
303 stop_take = std::make_shared<Looper::value_stop_take>();
306 void prepare(halp::setup)
308 if(ossia_document_context)
316 void stop() { m_stop_requested =
true; }
321 if(std::exchange(m_stop_requested,
false))
325 int64_t looper_run(int64_t start, int64_t length);
326 void looper_trim_recording(int64_t frames);
330 void operator()(
const tick& tk)
333 const auto timings = ossia_state.timings(tk);
334 m_tick_frames = timings.length;
335 m_tick_start = timings.start_sample;
336 if(std::exchange(m_stop_requested,
false))
338 if(tk.date == tk.prev_date)
344 m_now += m_tick_frames;
345 send_view(m_tick_frames);
351 halp_meta(layout, halp::layouts::hbox)
352 halp_meta(background, halp::colors::background_dark)
355 halp_meta(layout, halp::layouts::vbox)
358 halp_meta(layout, halp::layouts::vbox)
362 halp_meta(layout, halp::layouts::hbox)
363 halp::item<&ins::quantif> quantif;
364 halp::item<&ins::exported> exported;
369 halp_meta(name,
"Settings")
370 halp_meta(layout, halp::layouts::tabs)
371 halp_meta(background, halp::colors::background_darker)
374 halp_meta(name,
"Length")
375 halp_meta(layout, halp::layouts::grid)
376 halp_meta(alignment, halp::alignments::left)
377 halp_meta(spacing, 6)
378 halp_meta(columns, 3)
379 halp::item<&ins::after_record> after_record;
380 halp::item<&ins::postaction_bars> bars;
381 halp::item<&ins::late> late;
382 halp::item<&ins::capture>
capture;
383 halp::item<&ins::capture_bars> capture_bars;
387 halp_meta(name,
"Play")
388 halp_meta(layout, halp::layouts::grid)
389 halp_meta(alignment, halp::alignments::left)
390 halp_meta(spacing, 6)
391 halp_meta(columns, 2)
392 halp::item<&ins::start_from> start_from;
393 halp::item<&ins::passthrough> passthrough;
400 halp_meta(name,
"Overdub")
401 halp_meta(layout, halp::layouts::grid)
402 halp_meta(alignment, halp::alignments::left)
403 halp_meta(spacing, 6)
404 halp_meta(columns, 2)
405 halp::item<&ins::past_end> past_end;
406 halp::item<&ins::passes> passes;
407 halp::item<&ins::max_layers> max_layers;
408 halp::item<&ins::at_max_layers> at_max_layers;
415 halp_meta(layout, halp::layouts::container)
416 halp_meta(background, halp::colors::background_mid)
431 self.view.view.receive(msg);
442 static constexpr bool passes_wait_for_material =
true;
447 void layers_changing() noexcept { }
448 bool busy() const noexcept {
return false; }
449 void cleared() noexcept { }
450 void pass_dropped() noexcept
453 loop.touch_count = 0;
455 void begin_overdub(int64_t start,
bool oldest,
int replaced)
460 bool can_merge_newest() const noexcept {
return !loop.taking; }
461 void merge_newest() noexcept { loop.
merge(loop.
count - 2,
true); }
462 bool pass_closing() noexcept {
return true; }
464 void pass_closed() { loop.
end_pass(); }
465 void tick_started(
const tick&);
466 void prepare_loop() noexcept { }
467 void forget_carried() noexcept
469 for(
int i = 0; i < m_carried_count; i++)
470 m_carried[i].value.reset();
474 void tick_finished(
const tick&)
477 handle_input(m_tick_frames, m_tick_frames + 1, -1,
false);
479 void close_recording(int64_t bars)
483 loop.pass_written = loop.
length;
489 close_backwards(int64_t(std::ceil(m_phase - 1e-9)));
492 bool cut_after(int64_t end)
const noexcept {
return loop.taking && loop.pass_start >= end; }
493 void cut_pass_dropped() noexcept { }
494 void trim_top_pass(int64_t end);
495 void head_moved(int64_t pos)
noexcept
497 m_phase = double(pos);
498 m_expected_pos = pos;
500 double head_y(int64_t len)
const noexcept
502 const double l = double(len);
503 const double ph = std::fmod(std::max(0., m_phase), l);
504 double y = m_reverse ? l - ph : ph;
509 double rate_for(LoopMode)
const noexcept {
return rate(); }
510 bool by_quarters() const noexcept {
return inputs.follow_tempo.value &&
m_loop_tempo > 0.; }
512 void set_head(
double y, int64_t len)
noexcept
514 double h = m_reverse ? double(len) - y : y;
517 transport.playbackPos = std::clamp<int64_t>(int64_t(h), 0, len - 1);
519 int64_t capture_clock() const noexcept {
return m_now; }
520 int64_t capture_limit() const noexcept {
return std::numeric_limits<int64_t>::max(); }
521 void capture_into(int64_t end, int64_t n);
522 void captured() noexcept { head_moved(transport.playbackPos); }
523 bool capture_layer_ready(
const Looper::capture_window& w)
const noexcept;
524 void capture_layer_into(
const Looper::capture_window& w);
527 template <
typename F>
528 void for_each_captured(
const Looper::capture_window& w, F&& f)
const;
530 void stopping() noexcept
532 for(
auto& k : m_keys)
535 k.owned_until = std::numeric_limits<int64_t>::min();
538 void close_on_stop(
bool recording)
545 bool export_ready() const noexcept {
return true; }
546 void start_export(
int passes,
bool open,
bool recording);
547 void continue_export() noexcept { }
548 void export_item(
int item) { write_export_item(item); }
549 bool take_cancelled() noexcept {
return false; }
550 void take_released() noexcept { }
551 void stop_take_written() noexcept { }
552 const Looper::stop_take_notice& stop_notice() const noexcept {
return stop_take->notice; }
554 bool take_placeable() const noexcept {
return m_written_placeable; }
555 static constexpr const char* take_extension =
"values";
557 static constexpr halp::clip_stretch take_stretch() noexcept
559 return halp::clip_stretch::none;
561 bool view_full() const noexcept {
return loop.full; }
562 bool view_writing() const noexcept {
return m_writing; }
564 int max_layers() const noexcept
574 Looper::value_slot in;
575 Looper::value_slot out;
576 int64_t owned_until{std::numeric_limits<int64_t>::min()};
577 Looper::value_slot recorded;
579 std::array<key_state, max_keys> m_keys{};
580 void forget_recorded() noexcept
582 for(
auto& k : m_keys)
587 static constexpr int max_out = 1024;
591 Looper::value_slot value;
594 std::array<out_value, max_out> m_out{};
596 void emit(int64_t ts,
const ossia::value& v)
598 if(m_out_count < max_out)
600 auto& o = m_out[m_out_count++];
607 void send_held(uint16_t key,
const Looper::value_slot& v, int64_t ts)
609 auto& k = m_keys[key];
610 if(v.empty() || m_now + ts < k.owned_until)
612 if(!k.out.empty() && k.out.same(v))
620 static constexpr int max_in = 1024;
621 std::array<int, max_in> m_in_order{};
624 int64_t input_ts(
int i)
const noexcept;
635 static constexpr int max_due = 2048;
636 std::array<due, max_due> m_due{};
639 void add_due(int64_t ts,
int ref,
bool impulse)
noexcept
641 if(m_due_count < max_due)
643 m_due[m_due_count] = {ts, ref, impulse, m_due_count};
647 void send_due(int64_t until);
649 Looper::held_timeline m_timeline;
650 Looper::held_scratch m_scratch;
651 void update_timeline() { m_timeline.update(loop, 0, m_scratch); }
653 void chase(int64_t ts);
660 Looper::value_slot value;
663 std::array<carried_value, 1024> m_carried{};
664 int m_carried_count{};
665 int64_t m_carried_span{};
672 Looper::value_slot value;
675 static constexpr int history_size = 4096;
676 std::array<played, history_size> m_history{};
677 int64_t m_history_count{};
678 std::array<Looper::value_slot, max_keys> m_history_floor{};
681 int64_t m_tick_frames{};
682 int64_t m_tick_start{};
683 int64_t m_expected_pos{};
684 bool m_stop_requested{};
685 bool m_written_placeable{
true};
688 int64_t from, int64_t to,
double pos0,
bool taking,
double rate = 1.,
689 bool backwards =
false);
691 int64_t pos, int64_t written, uint16_t key,
const ossia::value& v,
bool backwards);
692 void close_backwards(int64_t boundary);
693 void next_cycle_pass(int64_t start);
694 void begin_pass_carried(int64_t k);
695 void begin_recording();
698 bool m_was_reverse{};
700 void advance(int64_t start, int64_t length,
bool overdub);
701 double rate() const noexcept;
702 void play_range(
double a,
double b,
double out0,
double r);
703 void play_range_reverse(
double a,
double b,
double out0,
double r);
705 void write_export_item(
int item);
707 const Looper::value_loop& snap,
int item, Looper::value_export& e);
708 void write_stop_take(
bool recording,
double date, int64_t pos);
711 Looper::value_loop m_export_loop;
712 Looper::held_timeline m_export_timeline;
713 void send_view(int64_t frames);
714 void fill_message(Looper::view_message& v);
717inline int64_t Node::input_ts(
int i) const noexcept
719 const auto& data = inputs.values.value->get_data();
720 return data[std::size_t(m_in_order[i])].timestamp - m_tick_start;
724inline void Node::tick_started(
const tick&)
728 if(!inputs.values.value)
730 const auto& data = inputs.values.value->get_data();
731 for(std::size_t i = 0; i < data.size() && m_in_count < max_in; i++)
732 if(data[i].value.valid())
733 m_in_order[m_in_count++] = int(i);
734 std::sort(m_in_order.begin(), m_in_order.begin() + m_in_count, [&](
int a,
int b) {
735 const auto ta = data[std::size_t(a)].timestamp, tb = data[std::size_t(b)].timestamp;
736 return ta != tb ? ta < tb : a < b;
740inline void Node::push_outputs()
742 for(
int i = 0; i < m_out_count; i++)
744 std::sort(m_out.begin(), m_out.begin() + m_out_count, [](
const out_value& a,
const out_value& b) {
745 return a.ts != b.ts ? a.ts < b.ts : a.order < b.order;
747 auto* port = outputs.values.value;
748 for(
int i = 0; i < m_out_count; i++)
752 m_out[i].value.get(),
753 m_tick_start + std::clamp<int64_t>(m_out[i].ts, 0, std::max<int64_t>(0, m_tick_frames - 1)));
754 m_out[i].value.reset();
760inline void Node::send_due(int64_t until)
762 for(; m_due_index < m_due_count && m_due[m_due_index].ts <= until; m_due_index++)
764 const auto& d = m_due[m_due_index];
767 if(d.ref < loop.event_count)
768 emit(d.ts, loop.events[d.ref].value.get());
771 const auto& p = m_timeline.points[d.ref];
772 if(p.event >= 0 && p.event < loop.event_count)
773 send_held(loop.events[p.event].key, loop.events[p.event].value, d.ts);
777inline void Node::chase(int64_t ts)
782 const int64_t x = std::clamp<int64_t>(int64_t(m_phase), 0, loop.
length - 1);
783 const int i = m_reverse ? m_timeline.before(x) : m_timeline.at(x);
786 const int e = m_timeline.points[i].event;
787 if(e >= 0 && e < loop.event_count)
788 send_held(loop.events[e].key, loop.events[e].value, ts);
794inline void Node::handle_input(
795 int64_t from, int64_t to,
double pos0,
bool taking,
double rate,
bool backwards)
797 const Passthrough pt = inputs.passthrough;
798 const bool echo = taking ? pt != Passthrough::None : pt == Passthrough::Full;
801 for(; m_in_index < m_in_count; m_in_index++)
803 const int64_t ts = input_ts(m_in_index);
807 const auto& v = inputs.values.value->get_data()[std::size_t(m_in_order[m_in_index])].value;
808 const uint16_t key = 0;
809 auto& k = m_keys[key];
810 const bool impulse = v.get_type() == ossia::val_type::IMPULSE;
813 auto& h = m_history[m_history_count++ % history_size];
814 if(m_history_count > history_size && !h.value.impulse())
815 m_history_floor[h.key] = h.value;
816 h.frame = m_now + ts;
829 const double into = (std::max(ts, from) - from) * rate;
830 pos = int64_t(pos0 + into);
832 if(backwards && loop.
length > 0)
838 else if(transport.actualMode != LoopMode::Record && loop.
length > 0)
846 const bool fresh = impulse || k.in.empty() || !k.in.same(v);
849 if(taking && pos >= 0 && fresh)
850 record_value(pos, written, key, v, backwards);
858 k.owned_until = m_now + ts + hold;
865inline void Node::record_value(
866 int64_t pos, int64_t written, uint16_t key,
const ossia::value& v,
bool backwards)
869 if(v.get_type() == ossia::val_type::IMPULSE)
874 auto& k = m_keys[key];
875 if(!loop.touched(key))
876 loop.
touch_key(key, pos, written, backwards);
883 else if(!k.recorded.empty())
884 loop.
add_value(pos, written, key, k.recorded.get());
890inline void Node::close_backwards(int64_t boundary)
895 for(
int key = 0; key <
max_keys; key++)
896 if(!m_keys[key].recorded.empty())
898 boundary, int64_t(loop.
pass_progress), uint16_t(key), m_keys[key].recorded.get());
903inline void Node::begin_recording()
905 loop.begin_recording();
907 for(
int key = 0; key <
max_keys; key++)
909 const auto& in = m_keys[key].in;
912 loop.
touch_key(uint16_t(key), 0, 0,
false);
913 loop.
add_value(0, 0, uint16_t(key), in.get());
917inline void Node::looper_trim_recording(int64_t frames)
919 const int64_t end = std::max<int64_t>(0, loop.
length - frames);
922 const int64_t cut = loop.
length - end;
923 for(
int i = 0; i < m_carried_count; i++)
924 m_carried[i].pos += cut;
925 m_carried_span += cut;
926 for(
int i = 0; i < loop.event_count; i++)
928 const auto& e = loop.events[i];
929 if(e.pos < end || e.impulse)
931 if(m_carried_count <
int(m_carried.size()))
933 auto& c = m_carried[m_carried_count++];
939 loop.
remove_events([end](
const Looper::value_event& e) {
return e.pos >= end; });
941 loop.pass_written = end;
949inline void Node::begin_pass_carried(int64_t k)
951 const int64_t len = loop.
length;
952 const bool from_point = m_carried_span > 0 && m_carried_span < len && k == m_carried_span;
954 for(
int i = 0; i < m_carried_count; i++)
956 const auto& c = m_carried[i];
957 const int64_t pos = from_point ? c.pos % len : k;
958 record_value(pos, from_point ? c.pos : 0, c.key, c.value.get(), false);
965inline int64_t Node::looper_run(int64_t start, int64_t length)
967 if(!loop.empty() && transport.playbackPos % loop.
length != m_expected_pos)
968 m_phase = double(transport.playbackPos % loop.
length);
969 if(m_reverse != m_was_reverse)
971 if(m_was_reverse && loop.taking && !loop.empty())
972 close_backwards(int64_t(std::ceil(m_phase - 1e-9)));
973 m_was_reverse = m_reverse;
976 const LoopMode mode = transport.actualMode;
980 handle_input(start, start + length, -1,
false);
984 advance(start, length,
false);
987 case LoopMode::Record: {
988 if(!transport.is_taking())
991 m_switches.soloed.forget();
992 transport.playbackPos = 0;
993 m_export_pending =
false;
995 transport.
taking(LoopMode::Record);
996 const int64_t pos0 = loop.
length;
997 handle_input(start, start + length,
double(pos0),
true);
999 loop.pass_written = loop.
length;
1002 transport.playbackPos = loop.
length;
1008 case LoopMode::Overdub: {
1011 handle_input(start, start + length, -1,
false);
1014 if(!transport.is_taking())
1016 const int64_t k = int64_t(m_phase) % loop.
length;
1017 begin_pass_carried(k);
1023 transport.
taking(LoopMode::Overdub);
1024 advance(start, length,
true);
1031 transport.playbackPos = int64_t(m_phase) % loop.
length;
1032 m_expected_pos = transport.playbackPos;
1039inline double Node::rate() const noexcept
1041 if(!inputs.follow_tempo.value || loop.empty() ||
m_loop_tempo <= 0.)
1048inline void Node::next_cycle_pass(int64_t start)
1059inline void Node::advance(int64_t start, int64_t length,
bool overdub)
1061 const double r = rate();
1062 const bool back = m_reverse;
1067 const double ph = std::fmod(std::max(0., m_phase),
double(loop.
length));
1069 double y = back ? double(loop.
length) - ph : ph;
1070 if(y >=
double(loop.
length))
1071 y -= double(loop.
length);
1072 double out = double(start);
1073 const double out_end = double(start + length);
1076 while(out < out_end - 1e-9)
1078 const double L = double(loop.
length);
1079 const double b = std::min(L, y + (out_end - out) * r);
1080 const double seg_out = (b - y) / r;
1082 next_cycle_pass(back ? 0 : int64_t(y));
1089 play_range_reverse(y, b, out, r);
1091 play_range(y, b, out, r);
1093 const int64_t from = int64_t(std::ceil(out - 1e-9));
1094 const int64_t to = b >= L ? int64_t(std::ceil(out + seg_out - 1e-9))
1095 : int64_t(
std::ceil(out_end - 1e-9));
1096 handle_input(from, to, y + (from - out) * r, overdub, r, back);
1097 send_due(std::numeric_limits<int64_t>::max());
1136 handle_input(start + length, start + length, -1,
false);
1139 y = double(loop.
length) - y;
1140 if(y >=
double(loop.
length))
1141 y -= double(loop.
length);
1150inline void Node::play_range(
double a,
double b,
double out0,
double r)
1155 const int64_t len = loop.
length;
1156 auto frame = [&](int64_t q) {
return int64_t(out0 + (q - a) / r); };
1157 const int64_t lo = int64_t(std::ceil(a));
1158 const int64_t hi = std::min(len, int64_t(std::ceil(b)));
1162 for(
int i = m_timeline.at(lo); i >= 0 && i < m_timeline.count; i++)
1164 const auto& p = m_timeline.points[i];
1169 add_due(frame(p.pos), i,
false);
1171 for(
int i = 0; i < loop.event_count; i++)
1173 const auto& e = loop.events[i];
1174 if(!e.impulse || !loop.
heard(e))
1176 const int64_t period = loop.period_for(e.pass);
1177 const int64_t first = e.pos % period;
1178 for(int64_t q = first + std::max<int64_t>(0, (lo - first + period - 1) / period) * period;
1179 q < hi; q += period)
1180 add_due(frame(q), i,
true);
1182 std::sort(m_due.begin(), m_due.begin() + m_due_count, [](
const due& x,
const due& y) {
1183 return x.ts != y.ts ? x.ts < y.ts : x.order < y.order;
1191inline void Node::play_range_reverse(
double a,
double b,
double out0,
double r)
1196 const int64_t len = loop.
length;
1197 auto frame = [&](int64_t q) {
return int64_t(out0 + (q - a) / r); };
1198 const int64_t lo = int64_t(std::ceil(a));
1199 const int64_t hi = std::min(len, int64_t(std::ceil(b)));
1202 auto mirrored = [](int64_t pos, int64_t period) {
1203 int64_t m = (-pos) % period;
1209 for(
int i = 0; i < m_timeline.count; i++)
1211 const int64_t q = mirrored(m_timeline.points[i].pos, len);
1212 if(q >= lo && q < hi)
1213 add_due(frame(q), m_timeline.previous(i),
false);
1215 for(
int i = 0; i < loop.event_count; i++)
1217 const auto& e = loop.events[i];
1218 if(!e.impulse || !loop.
heard(e))
1220 int64_t period = loop.period_for(e.pass);
1221 if(len % period != 0)
1223 const int64_t first = mirrored(e.pos % period, period);
1224 for(int64_t q = first + std::max<int64_t>(0, (lo - first + period - 1) / period) * period;
1225 q < hi; q += period)
1226 add_due(frame(q), i,
true);
1228 std::sort(m_due.begin(), m_due.begin() + m_due_count, [](
const due& x,
const due& y) {
1229 return x.ts != y.ts ? x.ts < y.ts : x.order < y.order;
1235inline void Node::capture_into(int64_t end, int64_t n)
1237 const int64_t from = end - n;
1238 loop.begin_recording();
1242 const int64_t count = std::min<int64_t>(m_history_count, history_size);
1244 for(
int key = 0; key <
max_keys; key++)
1246 const Looper::value_slot* before = m_history_floor[key].empty() ? nullptr : &m_history_floor[key];
1247 for(int64_t i = m_history_count - count; i < m_history_count; i++)
1249 const auto& h = m_history[i % history_size];
1250 if(h.key == key && h.frame < from && !h.value.impulse())
1254 record_value(0, 0, uint16_t(key), before->get(),
false);
1256 for(int64_t i = m_history_count - count; i < m_history_count; i++)
1258 const auto& h = m_history[i % history_size];
1259 if(h.frame < from || h.frame >= end)
1261 const int64_t pos = h.frame - from;
1262 const auto& last = m_keys[h.key].recorded;
1263 if(!h.value.impulse() && !last.empty() && last.same(h.value))
1265 record_value(pos, pos, h.key, h.value.get(),
false);
1267 loop.pass_written = n;
1272template <
typename F>
1273void Node::for_each_captured(
const Looper::capture_window& w, F&& f)
const
1275 const int64_t count = std::min<int64_t>(m_history_count, history_size);
1276 std::array<const Looper::value_slot*, max_keys> held{};
1277 for(
int key = 0; key <
max_keys; key++)
1278 if(!m_history_floor[key].empty())
1279 held[key] = &m_history_floor[key];
1280 for(int64_t i = m_history_count - count; i < m_history_count; i++)
1282 const auto& h = m_history[i % history_size];
1283 if(h.frame >= w.end)
1285 const bool impulse = h.value.impulse();
1286 const bool fresh = impulse || !held[h.key] || !held[h.key]->same(h.value);
1288 held[h.key] = &h.value;
1289 if(fresh && w.contains(h.frame))
1296inline bool Node::capture_layer_ready(
const Looper::capture_window& w)
const noexcept
1299 for_each_captured(w, [&any](
const played&) {
1309inline void Node::capture_layer_into(
const Looper::capture_window& w)
1311 for_each_captured(w, [&](
const played& h) {
1313 w.position(h.frame), w.progress(h.frame), h.key, h.value.get(), w.backwards);
1316 loop.pass_written = w.positions();
1320 close_backwards(w.position(w.end));
1324inline void Node::trim_top_pass(int64_t end)
1326 const int pass = loop.
count - 1;
1328 [=](
const Looper::value_event& e) {
return e.pass == pass && e.pos >= end; });
1331 loop.pass_written = std::clamp<int64_t>(end - loop.pass_start, 0, loop.pass_written);
1337inline void Node::start_export(
int passes,
bool open,
bool)
1339 m_export_loop.assign(loop);
1340 if(!open && passes < loop.
count)
1342 m_export_loop.count = passes;
1343 m_export_loop.total = passes;
1344 m_export_loop.taking =
false;
1345 m_export_loop.edits++;
1347 write_export_item(m_export_first);
1352inline void Node::write_export_item(
int item)
1354 m_export_item = item;
1356 fill_export(m_export_loop, item, *export_data);
1358 Looper::value_take_job job;
1359 job.writer = writer;
1360 job.data = export_data;
1361 job.slot = m_export_slot;
1362 job.number = m_export_number;
1364 worker.request(std::move(job));
1370inline void Node::write_stop_take(
bool recording,
double date, int64_t)
1373 if(!writer || loop.empty() || s.in_use)
1375 s.loop.assign(loop);
1378 s.meter_numerator = m_signature.upper;
1379 s.meter_denominator = m_signature.lower;
1382 Looper::value_take_job job;
1383 job.writer = writer;
1385 job.slot = claim_take_slot();
1388 worker.request(std::move(job));
1393inline void Node::fill_export(
1394 const Looper::value_loop& snap,
int item, Looper::value_export& e)
1398 e.tempo = m_export_tempo;
1399 e.meter_numerator = m_export_signature.upper;
1400 e.meter_denominator = m_export_signature.lower;
1403inline void Node::send_view(int64_t frames)
1407 fill_message(m_view);
1408 send_message(m_view);
1412inline void Node::fill_message(Looper::view_message& v)
1416 v.lanes = std::min(loop.
count, Looper::view_message::max_lanes);
1417 v.max_seconds = 0.f;
1420 constexpr int bins = Looper::view_message::bins;
1421 const int lanes = v.lanes;
1422 const int merged = loop.
count - lanes;
1423 const int64_t len = std::max<int64_t>(1, loop.
length);
1425 v.value_flags.fill(0);
1432 m_scratch.prepare(loop, 0,
false);
1433 for(
int l = 0; l < lanes; l++)
1435 const int first = l == 0 ? 0 : l + merged;
1436 const int last = l + merged;
1437 float* y = v.peaks.data() + l * bins;
1438 uint8_t* f = v.value_flags.data() + l * bins;
1439 double lo = 0., hi = 0.;
1441 for(
int b = 0; b < bins; b++)
1443 const int64_t x = std::min<int64_t>(len - 1, int64_t(
double(b) *
double(len) / bins));
1444 const int e = m_scratch.value_at(loop, 0, x, first, last);
1447 const auto& val = loop.events[e].value.get();
1448 const int at = m_timeline.at(x);
1449 if(at >= 0 && m_timeline.points[at].event == e)
1456 lo = any ? std::min(lo, n) : n;
1457 hi = any ? std::max(hi, n) : n;
1466 for(
int b = 0; b < bins; b++)
1468 y[b] = hi > lo ? float((y[b] - lo) / (hi - lo)) : 0.5f;
1473 for(
int i = 0; i < loop.event_count; i++)
1475 const auto& e = loop.events[i];
1476 if(e.pass >= loop.
count)
1481 const int l = std::clamp(e.pass - merged, 0, lanes - 1);
1482 const int64_t period = loop.period_for(e.pass);
1483 for(int64_t q = e.pos % period; q < len; q += period)
1485 const int b = std::clamp(
int(q * bins / len), 0, bins - 1);
1486 auto& f = v.value_flags[l * bins + b];
1488 if(e.impulse && loop.
heard(e))
PastEnd
What an overdub does at the end of the loop.
Definition score-plugin-fx/Fx/Looper/Transport.hpp:197
@ Extend
Makes the loop longer by the loop's length, the passes under it repeating.
@ Wrap
Goes round the loop again.
TakeFiles
Definition LooperView.hpp:46
void fill_value_export(const value_loop &snap, int item, held_timeline &tl, held_scratch &scratch, value_export &e)
Definition ValueTake.hpp:374
Passthrough
Definition score-plugin-fx/Fx/Looper/Transport.hpp:189
Passes
What an overdub that goes round the loop several times leaves.
Definition score-plugin-fx/Fx/Looper/Transport.hpp:131
@ PerCycle
A pass for each time it goes round, cut on the loop's start.
@ Whole
One pass, from when the overdub starts to when it stops.
Definition Switches.hpp:344
Definition Switches.hpp:161
Definition Switches.hpp:300
An enumeration shown as a combobox: compact where the choices are many.
Definition Switches.hpp:59
Definition LooperView.hpp:64
Definition Switches.hpp:50
Definition Switches.hpp:828
Definition LooperNode.hpp:169
int64_t m_extend_unit
Definition LooperNode.hpp:398
void stop_score()
Definition LooperNode.hpp:990
void begin_pass(int64_t start)
Definition LooperNode.hpp:677
bool m_cycle_pending
Definition LooperNode.hpp:402
void material_arriving()
Definition LooperNode.hpp:237
void close_pass(bool cycle=false)
Definition LooperNode.hpp:705
void pass_advanced(double frames, bool backwards)
The pass being taken in went over frames more frames of the loop.
Definition LooperNode.hpp:251
double pass_progress() const noexcept
The frames the pass being taken in went over.
Definition LooperNode.hpp:262
void prepare_stop_notice(Looper::stop_take_notice &n, double date)
Definition LooperNode.hpp:1021
bool pass_pending() const noexcept
Definition LooperNode.hpp:219
void view_request(const Looper::erase_request &r) noexcept
From the view: applied at the start of the next tick.
Definition LooperNode.hpp:285
double m_speed
The playhead goes at m_speed, backwards when m_reverse is set.
Definition LooperNode.hpp:389
double m_loop_tempo
Tempo the loop was recorded at: the loop keeps its own speed.
Definition LooperNode.hpp:395
void capture(const tick &tk)
Definition LooperNode.hpp:888
void fill_view(Looper::view_message &v) const noexcept
Definition LooperNode.hpp:1177
bool view_due(int64_t frames) noexcept
Whether the view is due a message: some 30 a second.
Definition LooperNode.hpp:358
Definition LooperView.hpp:1331
static constexpr int max_layers
Most layers kept: Max layers is at most this.
Definition PassStack.hpp:83
int64_t length
Frames in the loop.
Definition PassStack.hpp:94
int count
Passes heard.
Definition PassStack.hpp:90
bool extend(int64_t unit, int64_t max_length, bool top_open=true) noexcept
Definition PassStack.hpp:227
The grid a change of mode waits for. The Quantif of older scores.
Definition Switches.hpp:232
Definition score-plugin-fx/Fx/Looper/Transport.hpp:166
Definition Switches.hpp:850
Definition Switches.hpp:194
Definition Switches.hpp:329
Definition Switches.hpp:778
void merge(int at, bool undoable=false) noexcept
Definition PassStack.hpp:1032
void begin_overdub(int64_t start, int max=stack::max_layers, bool oldest=true, int replace=-1) noexcept
Definition PassStack.hpp:1004
uint64_t edits
Changes each time what is heard of the loop may have changed.
Definition PassStack.hpp:889
bool pass_backwards
Definition PassStack.hpp:899
double pass_progress
Definition PassStack.hpp:896
static std::shared_ptr< take_writer > make(const score::DocumentContext &ctx, std::string_view prefix, std::string_view extension)
Main thread only.
Definition TakeWriter.hpp:70
void taking(LoopMode m) noexcept
Called by the looper whenever it records or overdubs.
Definition score-plugin-fx/Fx/Looper/Transport.hpp:336
static double number(const ossia::value &v, int component) noexcept
Number component of a value, which it has.
Definition ValueTake.hpp:127
static int numbers_of(const ossia::value &v) noexcept
Definition ValueTake.hpp:101
Definition ValueLoop.hpp:117
void remove_events(F &&gone) noexcept
The values gone says are let go of.
Definition ValueLoop.hpp:193
value_event * add_value(int64_t pos, int64_t written, uint16_t key, const ossia::value &v) noexcept
Definition ValueLoop.hpp:323
void update_open_stretches() noexcept
Definition ValueLoop.hpp:266
void end_pass() noexcept
Definition ValueLoop.hpp:296
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
void write_over(int64_t lo, int64_t hi, double at) noexcept
Definition ValueLoop.hpp:364
Definition ValueTake.hpp:414
Definition LooperView.hpp:77
@ flag_number
A number is held, at the height in peaks.
Definition LooperView.hpp:111
@ flag_impulse
An impulse fires in the bin.
Definition LooperView.hpp:114
@ flag_heard_mark
The impulse is heard.
Definition LooperView.hpp:116
@ flag_mark
A value that is not a number starts in the bin.
Definition LooperView.hpp:115
@ flag_heard
What is held is what the loop plays there.
Definition LooperView.hpp:113
@ flag_other
Something else is held: text, a list, a map.
Definition LooperView.hpp:112
Definition ValueLooper.hpp:47
Definition ValueLooper.hpp:421
Definition ValueLooper.hpp:350
Definition ValueLooper.hpp:261
static bool must_run(const Looper::value_take_job &job) noexcept
The take the score's stop leaves is written once the looper is gone.
Definition ValueLooper.hpp:264
Definition ValueLooper.hpp:22
~Node()
Definition ValueLooper.hpp:319
std::shared_ptr< Looper::value_stop_take > stop_take
Definition ValueLooper.hpp:298
static constexpr double max_loop_seconds
Longest loop an extending overdub makes, as the audio looper's longest.
Definition ValueLooper.hpp:328
static constexpr double input_hold_seconds
Definition ValueLooper.hpp:44
void stop()
Definition ValueLooper.hpp:316
static constexpr int max_keys
Keys of the values looped: one stream for now.
Definition ValueLooper.hpp:41
void process_message(const Looper::erase_request &r)
From the view: applied at the start of the next tick.
Definition ValueLooper.hpp:288
std::function< void(Looper::view_message)> send_message_from_worker
Definition ValueLooper.hpp:285
Definition MIDISync.hpp:149
Definition DocumentContext.hpp:18