2#include <Fx/Looper/LooperView.hpp>
3#include <Fx/Looper/Switches.hpp>
4#include <Fx/Looper/TakeWriter.hpp>
5#include <Fx/Looper/Transport.hpp>
7#include <ossia/dataflow/token_request.hpp>
134 static int64_t wrap(int64_t k, int64_t len)
noexcept
137 return k < 0 ? k + len : k;
142 return std::clamp<int64_t>(std::llround(
double(n) * rate), 0, length);
147 const auto y = int64_t(std::floor(y_end));
148 return backwards ? wrap(length - y, length) : wrap(y -
positions(), length);
153 double y = std::fmod(y_end -
double(end - t) * rate,
double(length));
156 const int64_t k = std::min(int64_t(y), length - 1);
157 return backwards ? wrap(length - k, length) : k;
162 return std::clamp<int64_t>(int64_t(
double(t - (end - n)) * rate), 0,
positions());
164 bool contains(int64_t t)
const noexcept {
return t >= end - n && t < end; }
167template <
typename Node>
170 using LoopMode = Looper::LoopMode;
171 using tick = ossia::token_request;
179 void looper_close_take(
180 const ossia::token_request& tk, LoopMode mode,
bool ended_by_postaction);
187 m_switches.
apply(self().inputs, self());
189 m_reverse = self().inputs.reverse.value;
193 int looper_passes() const noexcept {
return stack().count; }
194 bool looper_muted(
int pass)
const noexcept {
return stack().layers[pass].muted; }
195 void looper_set_muted(
int pass,
bool muted)
197 if(stack().layers[pass].muted == muted)
200 stack().set_muted(pass, muted);
202 void looper_mute(
int pass) {
mute_pass(pass); }
203 void looper_erase(
int pass) {
erase_pass(pass); }
206 static constexpr bool lazy_passes() noexcept
208 if constexpr(
requires { Node::passes_wait_for_material; })
209 return Node::passes_wait_for_material;
226 begin_pass_now(m_pending_start);
229 if(
auto* top = stack().top())
230 top->loop_before = m_pending_length;
231 if constexpr(lazy_passes())
232 if(m_pending_progress > 0.)
233 stack().advance_pass(m_pending_progress, m_pending_backwards);
240 m_pending_progress += frames;
241 m_pending_backwards = backwards;
243 else if constexpr(lazy_passes())
244 stack().advance_pass(frames, backwards);
250 return m_pending_progress;
251 if constexpr(lazy_passes())
252 return stack().pass_progress;
261 self().layers_changing();
264 Node& self() noexcept {
return static_cast<Node&
>(*this); }
265 const Node& self() const noexcept {
return static_cast<const Node&
>(*this); }
266 auto& stack() noexcept {
return self().stack(); }
267 const auto& stack() const noexcept {
return self().stack(); }
272 if(r.take > 0 && r.last >= 0)
283 m_erase_requested =
true;
286 void run_tick(
const tick& tk);
287 void apply_buttons();
288 void follow_merges() { m_switches.
follow_merges(self().inputs, stack().merged_passes); }
297 void begin_pass_now(int64_t start);
299 void merge_extra_pass();
301 void capture_layer(
const tick& tk, int64_t now, int64_t end);
310 const double len = double(stack().length);
313 = self().by_quarters()
315 : (now.frames -
m_origin.frames) / len;
320 void take_written(
int slot,
int number,
bool ok,
bool alone);
324 return m_view.
take > 0 && m_view.take_mix && m_view.take_layers >= stack().count
325 && m_view.take_version ==
m_version && !m_export_pending
326 && !self().view_writing();
329 static uint64_t next_looper_id() noexcept
331 static std::atomic<uint64_t> ids{};
334 int claim_take_slot() noexcept
336 const int slot = m_next_take_slot;
344 if(!self().send_message)
346 m_view_countdown -= frames;
347 if(m_view_countdown > 0)
349 m_view_countdown = int64_t(m_rate / 30.);
357 if constexpr(
requires(
const Node& n) { n.take_placeable(); })
358 return self().take_placeable();
363 double m_rate{48000.};
364 double m_tempo{120.};
365 ossia::time_signature m_signature{4, 4};
391 bool m_export_pending{};
393 double m_export_date{};
397 double m_export_tempo{120.};
398 ossia::time_signature m_export_signature{4, 4};
400 int m_export_layers{};
402 int m_export_number{};
407 halp::clip_stretch m_stop_stretch{};
408 int m_takes_written{};
417 int m_export_first{};
425 int64_t m_pending_start{};
426 int64_t m_pending_length{};
427 double m_pending_progress{};
428 bool m_pending_backwards{};
429 int m_next_take_slot{};
431 int64_t m_view_countdown{};
433 uint64_t
m_id{next_looper_id()};
436 Looper::erase_request m_erase_request{};
437 bool m_erase_requested{};
440template <
typename Node>
441void looper_node<Node>::run_tick(
const tick& tk)
443 auto& in = self().inputs;
445 const auto state = self().ossia_state;
446 m_rate = state.sampleRate();
448 m_signature = tk.signature;
450 m_reverse = in.reverse.value;
451 self().tick_started(tk);
457 self().prepare_loop();
459 const bool empty = st.empty() || self().busy();
460 Looper::read_loop(in.mode, transport, m_loop, empty);
461 m_switches.read(in, transport, self(), empty);
464 m_mode = transport.
wanted;
465 const Looper::transport_settings settings{
467 .quantif = in.quantif.value,
468 .after_record = in.after_record,
469 .postaction_bars = in.postaction_bars,
470 .start_from = in.start_from,
471 .late_seconds = in.late.value / 1000.,
472 .stop_at = in.stop_at.value};
473 transport(tk, state, settings, self());
475 self().forget_carried();
478 m_switches.follow(in, st.count);
480 self().tick_finished(tk);
484 self().outputs.phase.value
485 = st.length > 0 ? float(transport.playbackPos % st.length) / float(st.length) : 0.f;
488template <
typename Node>
489void looper_node<Node>::apply_buttons()
491 auto& in = self().inputs;
496 if(std::exchange(m_erase_requested,
false))
498 {m_erase_request.mute ? Looper::layer_action::mute : Looper::layer_action::erase,
499 m_erase_request.pass, m_erase_request.layers},
502 if(in.undo.value || in.redo.value)
503 m_switches.soloed.forget();
515 if(in.redo.value && !pass_open())
518 const int out = stack().leaves_on_redo();
522 m_switches.removed(in, out);
530template <
typename Node>
533 m_switches.soloed.forget();
537 m_pass_pending =
false;
548template <
typename Node>
552 if(
transport.actualMode == LoopMode::Record)
560 self().pass_dropped();
569template <
typename Node>
572 const int back = stack().returns_on_undo();
573 if(stack().undo() && back >= 0)
574 m_switches.inserted(self().inputs, back, stack().count);
579template <
typename Node>
583 if(pass < 0 || pass >= st.count || self().busy())
585 m_switches.soloed.forget();
588 else if(pass_open() && pass == st.count - 1)
594 m_switches.removed(self().inputs, pass);
599template <
typename Node>
603 if(pass < 0 || pass >= st.count || self().busy())
605 m_switches.soloed.forget();
607 st.set_muted(pass, !st.layers[pass].muted);
613template <
typename Node>
618 || st.count < self().max_layers() || st.count < 2)
620 int pass = st.count - 1;
621 for(
int i = 0; i < st.count; i++)
622 if(st.layers[i].muted)
627 m_switches.soloed.forget();
629 m_switches.removed(self().inputs, pass);
635template <
typename Node>
638 if constexpr(lazy_passes())
640 m_pass_pending =
true;
641 m_pending_start = start;
642 m_pending_length = stack().length;
643 m_pending_progress = 0.;
644 m_pending_backwards =
false;
647 begin_pass_now(start);
650template <
typename Node>
654 const int replaced = make_room();
655 self().begin_overdub(
663template <
typename Node>
667 if(std::exchange(m_pass_pending,
false))
673 auto* top = stack().top();
681 m_export_pending =
true;
682 if(!self().pass_closing())
684 top->loop_after = stack().length;
685 self().pass_closed();
689template <
typename Node>
693 && stack().count > self().max_layers() && self().can_merge_newest())
696 self().merge_newest();
700template <
typename Node>
701void looper_node<Node>::looper_close_take(
702 const ossia::token_request& tk, LoopMode mode,
bool ended_by_postaction)
708 if(mode == LoopMode::Record)
710 m_loop_tempo = transport.
loop_tempo(tk, st.length);
711 const double bar_frames = m_rate * 4. * (double(tk.signature.upper) / tk.signature.lower)
713 const auto bars = int64_t(std::floor(transport.postaction_bars * bar_frames));
720 const bool to_bars = ended_by_postaction && transport.quantized()
721 && transport.postaction_bars > 0 && bars > 0;
722 if(to_bars && bars < st.length)
723 self().looper_trim_recording(st.length - bars);
724 self().close_recording(to_bars ? bars : 0);
725 m_export_date = transport.
recordStartDate.impl / double(ossia::flicks_per_second<int64_t>);
729 const auto at = double(transport.playbackPos % std::max<int64_t>(1, st.length));
730 m_origin = transport.timeline(tk);
731 m_origin.frames -= at;
732 m_origin.quarters -= at / m_rate * m_loop_tempo / 60.;
739 self().close_overdub();
743 self().close_overdub();
747 m_export_pending =
true;
752template <
typename Node>
756 if(!m_extended || m_extend_unit <= 0 || st.count == 0)
758 const int64_t end = st.length - m_extend_unit;
759 const int64_t into_last =
transport.playbackPos - end;
760 if(into_last < 0 || into_last > max_frames || end < m_extend_unit)
765 m_pass_pending =
false;
766 self().cut_pass_dropped();
768 if(
auto* t = st.top())
771 else if(st.count > 1 && self().cut_after(end))
776 self().pass_dropped();
779 self().cut_pass_dropped();
781 if(
auto* t = st.top())
786 self().trim_top_pass(end);
791 self().head_moved(into_last);
796template <
typename Node>
800 const auto& st = stack();
801 if(st.empty() || self().busy() || (m != LoopMode::Play && m != LoopMode::Overdub))
813template <
typename Node>
818 const auto& st = stack();
819 if(st.empty() || self().busy() || st.length <= 0 || m == LoopMode::Record)
821 const double rate = self().rate_for(m);
823 if(m == LoopMode::Stop)
825 round, int64_t(std::floor(rounds_since_made(tk) *
double(round) + 1e-6)),
false};
827 return {round, std::clamp<int64_t>(round - to_end, 0, round),
true};
832template <
typename Node>
835 const auto& st = stack();
838 const double rounds = rounds_since_made(tk);
841 std::floor((rounds - std::floor(rounds)) *
double(st.length) + 1e-6), st.length);
847template <
typename Node>
850 if(
transport.is_taking() || self().busy())
852 auto& in = self().inputs;
853 const double bar = m_rate * 4. * (double(tk.signature.upper) / tk.signature.lower)
854 * (60. / std::max(1., tk.tempo));
855 const int64_t now = self().capture_clock();
860 if(late >= 0 && late <= int64_t(in.late.value * m_rate / 1000.))
865 capture_layer(tk, now, end);
868 const int64_t n = std::min<int64_t>(
869 {int64_t(in.capture_bars.value * bar), end, self().capture_limit()});
874 m_switches.soloed.forget();
875 self().capture_into(end, n);
878 m_loop_tempo = tk.tempo;
880 m_origin.frames -= double(
transport.playbackPos);
881 m_origin.quarters -= double(
transport.playbackPos) / m_rate * tk.tempo / 60.;
882 const double model_per_frame = self().ossia_state.samplesToModel();
883 m_export_date = (tk.prev_date.impl - (now - end + n) * model_per_frame)
884 / double(ossia::flicks_per_second<int64_t>);
885 m_export_pending =
true;
895template <
typename Node>
899 const int64_t len = st.length;
901 const bool stopped = m != LoopMode::Play;
902 const double rate = self().rate_for(m);
903 if(len <= 0 || !(rate > 0.))
908 const double rounds = rounds_since_made(tk);
909 y_now = std::floor((rounds - std::floor(rounds)) *
double(len) + 1e-6);
912 y_now = self().head_y(len);
914 const auto round = int64_t(std::ceil(
double(len) / rate - 1e-9));
915 double y_end = std::fmod(y_now -
double(now - end) * rate,
double(len));
917 y_end += double(len);
920 .n = std::min<int64_t>({round, end, self().capture_limit()}),
924 .backwards = m_reverse};
925 if(w.n <= 0 || w.positions() <= 0 || !self().capture_layer_ready(w))
929 const auto pushes = st.pushes;
930 begin_pass_now(w.start());
931 if(st.pushes == pushes)
933 self().capture_layer_into(w);
938 self().set_head(y_now, len);
949template <
typename Node>
954 const bool taking = pass_open();
955 m_pass_pending =
false;
956 const bool recording = taking &&
transport.actualMode == LoopMode::Record;
960 const int64_t pos = st.length > 0 ?
transport.playbackPos % st.length : 0;
963 self().close_on_stop(recording);
964 const bool take = !st.empty() && (taking || !last_take_whole());
972 m_stop_stretch = self().take_stretch();
973 self().write_stop_take(recording, date, pos);
975 m_view_countdown = 0;
980template <
typename Node>
984 self().fill_message(n.view);
985 n.view.stopped =
true;
986 n.take = m_takes_written + 1;
988 n.stretch = m_stop_stretch;
989 n.extension = Node::take_extension;
997template <
typename Node>
1001 const bool asked = m_take_request > m_view.answered;
1002 if(asked && st.empty())
1005 m_view.answered = m_take_request;
1006 m_view_countdown = 0;
1010 auto& part = m_part_request;
1011 const bool part_asked = part.take > m_view.part_answered;
1013 && (st.empty() || part.layers != st.count || part.pass < 0 || part.last < part.pass
1014 || part.last >= st.count))
1016 m_view.part_answered = part.take;
1018 m_view_countdown = 0;
1021 part_asked && !m_writing && self().writer && self().export_ready())
1026 m_job_request = part.take;
1028 const bool open = pass_open();
1029 const bool recording = open &&
transport.actualMode == LoopMode::Record;
1030 m_job_stretch = self().take_stretch();
1031 m_export_tempo = recording ? m_tempo : m_loop_tempo;
1032 m_export_signature = m_signature;
1033 m_export_mix =
false;
1034 m_export_first = part.pass;
1035 m_export_layers = part.last + 1;
1036 m_export_number = 0;
1037 m_export_slot = claim_take_slot();
1038 self().start_export(st.count, open, recording);
1041 (m_export_pending || asked) && !m_writing && self().writer && !st.empty()
1042 && self().export_ready())
1044 m_export_pending =
false;
1046 m_job_request = m_take_request;
1047 m_job_version = m_version;
1048 const bool open = asked && pass_open();
1049 const bool recording = open &&
transport.actualMode == LoopMode::Record;
1052 m_job_date = m_export_date;
1053 m_job_stretch = self().take_stretch();
1054 m_export_tempo = recording ? m_tempo : m_loop_tempo;
1055 m_export_signature = m_signature;
1057 = open || !pass_open() ? st.count : std::max(1, st.count - 1);
1060 const auto files = self().inputs.exported.value;
1061 m_export_mix = asked || files != Looper::TakeFiles::Layers;
1062 m_export_layers = asked || files != Looper::TakeFiles::Mix ? passes : 0;
1063 m_export_first = m_export_mix ? -1 : 0;
1064 m_export_number = 0;
1065 m_export_slot = claim_take_slot();
1066 self().start_export(passes, open, recording);
1068 self().continue_export();
1071template <
typename Node>
1074 auto& writer = self().writer;
1077 self().stop_take_written();
1082 const auto& v = self().stop_notice().view;
1085 v.take_base.data(), v.take_ext.data(), v.take_mix, v.take_layers, v.take_stretch,
1087 m_view.take = m_takes_written;
1088 m_view.take_version = v.take_version;
1089 m_view.take_date = v.take_date;
1090 m_view_countdown = 0;
1094 if(self().take_cancelled() || !ok || !writer)
1098 self().take_released();
1099 auto& answered = m_job_part ? m_view.part_answered : m_view.answered;
1100 answered = std::max(answered, m_job_request);
1101 m_view_countdown = 0;
1105 m_export_number = number;
1106 if(m_export_item + 1 < m_export_layers)
1108 self().export_item(m_export_item + 1);
1112 self().take_released();
1116 writer->take_base(slot), m_export_first, m_export_layers - 1, m_job_request,
1117 m_job_stretch, placeable_take());
1118 m_view.part_answered = std::max(m_view.part_answered, m_job_request);
1119 m_view_countdown = 0;
1123 m_view.take_version = m_job_version;
1125 writer->take_base(slot), Node::take_extension, m_export_mix, m_export_layers,
1126 m_job_stretch, placeable_take());
1127 m_view.take = m_takes_written;
1128 m_view.take_date = m_job_date;
1129 m_view.answered = std::max(m_view.answered, m_job_request);
1131 m_view_countdown = 0;
1136template <
typename Node>
1139 const auto& st = stack();
1140 v.empty = st.empty();
1142 v.requested = int(
transport.quantizedPlayMode);
1143 v.wanted = int(m_mode);
1144 v.layers = st.count;
1145 v.redo = st.total - st.count;
1146 v.full = self().view_full();
1147 v.length_seconds = float(st.length / m_rate);
1149 = st.length > 0 ? float(
transport.playbackPos % st.length) / float(st.length) : 0.f;
1150 v.tempo = float(m_tempo);
1151 v.writing = self().view_writing();
1152 v.version = m_version;
1153 v.reverse = m_reverse;
1154 v.speed = float(m_speed);
1155 v.selected = self().inputs.layer.value - 1;
1160 v.new_pass_pending =
transport.new_pass_pending();
1161 const int lanes = std::min(st.count, Looper::view_message::max_lanes);
1162 const int merged = st.count - lanes;
1164 for(
int l = merged > 0 ? 1 : 0; l < lanes; l++)
1165 v.muted[l] = st.layers[l + merged].muted;
Definition AudioLayers.hpp:14
@ Extend
Makes the loop longer by the loop's length, the passes under it repeating.
@ MergeOldest
The two oldest layers are merged into one.
@ MergeIntoNewest
The pass is merged into the newest layer once it ends.
double clamp_speed(float s) noexcept
A speed the loopers can play at, whatever a port sends.
Definition score-plugin-fx/Fx/Looper/Transport.hpp:181
transport::timeline_point timeline_start(const ossia::token_request &tk, ossia::exec_state_facade st) noexcept
Definition score-plugin-fx/Fx/Looper/Transport.hpp:1052
void show_loop(Field &f, const transport &t, loop_input &l)
Mode shows what is asked for, once the tick has run.
Definition Switches.hpp:145
int64_t frames_to_loop_end(double y, int64_t len, double rate) noexcept
Definition score-plugin-fx/Fx/Looper/Transport.hpp:248
int64_t loop_round(int64_t len, double rate) noexcept
Definition score-plugin-fx/Fx/Looper/Transport.hpp:240
Definition LooperNode.hpp:126
int64_t start() const noexcept
The first position of what it goes over, in the loop's own order.
Definition LooperNode.hpp:145
int64_t positions() const noexcept
Positions of the loop the window goes over, a round at most.
Definition LooperNode.hpp:140
int64_t progress(int64_t t) const noexcept
The positions gone over from the window's start to input frame t.
Definition LooperNode.hpp:160
int64_t position(int64_t t) const noexcept
The loop position input frame t falls on, as an overdub records it.
Definition LooperNode.hpp:151
Definition LooperView.hpp:61
Definition score-plugin-fx/Fx/Looper/Transport.hpp:228
Definition LooperNode.hpp:169
void clear_loop()
Definition LooperNode.hpp:531
int64_t m_extend_unit
Definition LooperNode.hpp:382
double m_job_date
Definition LooperNode.hpp:396
int m_take_request
Definition LooperNode.hpp:411
uint64_t m_id
Tells this looper's messages from another's, and counts its stops.
Definition LooperNode.hpp:433
void looper_rephase(const ossia::token_request &tk)
Definition LooperNode.hpp:833
void stop_score()
Definition LooperNode.hpp:950
void capture_layer(const tick &tk, int64_t now, int64_t end)
Definition LooperNode.hpp:896
int m_version
Definition LooperNode.hpp:420
bool pass_open() const noexcept
A pass is open in the stack.
Definition LooperNode.hpp:218
bool placeable_take() const noexcept
Definition LooperNode.hpp:355
void begin_pass(int64_t start)
Definition LooperNode.hpp:636
halp::clip_stretch m_job_stretch
Definition LooperNode.hpp:406
LoopMode m_mode
The mode asked for, as the transport takes it up.
Definition LooperNode.hpp:367
void passes_changing()
The passes, or what is heard of them, are about to change.
Definition LooperNode.hpp:258
bool m_cycle_pending
Definition LooperNode.hpp:386
void undo_pass()
Definition LooperNode.hpp:570
void material_arriving()
Definition LooperNode.hpp:222
bool m_pass_pending
Definition LooperNode.hpp:424
double rounds_since_made(const ossia::token_request &tk) const noexcept
Definition LooperNode.hpp:308
void close_pass(bool cycle=false)
Definition LooperNode.hpp:664
void pass_advanced(double frames, bool backwards)
The pass being taken in went over frames more frames of the loop.
Definition LooperNode.hpp:236
double pass_progress() const noexcept
The frames the pass being taken in went over.
Definition LooperNode.hpp:247
void erase_pass(int pass)
Definition LooperNode.hpp:580
void prepare_stop_notice(Looper::stop_take_notice &n, double date)
Definition LooperNode.hpp:981
int64_t looper_trim_extension(int64_t max_frames)
Definition LooperNode.hpp:753
bool pass_pending() const noexcept
Definition LooperNode.hpp:216
void run_export()
Definition LooperNode.hpp:998
void view_request(const Looper::erase_request &r) noexcept
From the view: applied at the start of the next tick.
Definition LooperNode.hpp:270
void mute_pass(int pass)
A muted pass is kept as it is; muting it again lets it be heard.
Definition LooperNode.hpp:600
void loop_arrived() noexcept
A value came in on Mode.
Definition LooperNode.hpp:176
Looper::transport::timeline_point m_origin
Definition LooperNode.hpp:377
double m_speed
The playhead goes at m_speed, backwards when m_reverse is set.
Definition LooperNode.hpp:373
Looper::erase_request m_part_request
Definition LooperNode.hpp:415
Looper::loop_input m_loop
What came in on Mode, and what it shows.
Definition LooperNode.hpp:369
double m_loop_tempo
Tempo the loop was recorded at: the loop keeps its own speed.
Definition LooperNode.hpp:379
void drop_taken_pass()
Definition LooperNode.hpp:549
Looper::loop_period looper_period(const ossia::token_request &tk) const noexcept
Definition LooperNode.hpp:815
int64_t looper_frames_to_end() const noexcept
An extending overdub grows the loop at its end instead of reaching it.
Definition LooperNode.hpp:797
bool last_take_whole() const noexcept
The last take has the mix and every pass as they are now.
Definition LooperNode.hpp:322
void capture(const tick &tk)
Definition LooperNode.hpp:848
void fill_view(Looper::view_message &v) const noexcept
Definition LooperNode.hpp:1137
bool view_due(int64_t frames) noexcept
Whether the view is due a message: some 30 a second.
Definition LooperNode.hpp:342
int make_room()
Definition LooperNode.hpp:614
Definition TakeWriter.hpp:153
Definition Switches.hpp:529
void apply(Inputs &in, Engine &e)
On the grid point.
Definition Switches.hpp:615
void follow_merges(Inputs &in, Log &log)
Definition Switches.hpp:691
static constexpr int slots
Definition TakeWriter.hpp:47
Definition score-plugin-fx/Fx/Looper/Transport.hpp:317
Definition score-plugin-fx/Fx/Looper/Transport.hpp:269
double loop_tempo(const ossia::token_request &tk, int64_t frames) const noexcept
Definition score-plugin-fx/Fx/Looper/Transport.hpp:302
ossia::time_value recordStartDate
Model date at which the current recording started.
Definition score-plugin-fx/Fx/Looper/Transport.hpp:295
void stopped(bool empty) noexcept
Definition score-plugin-fx/Fx/Looper/Transport.hpp:481
void restart_recording() noexcept
Definition score-plugin-fx/Fx/Looper/Transport.hpp:437
void taking(LoopMode m) noexcept
Called by the looper whenever it records or overdubs.
Definition score-plugin-fx/Fx/Looper/Transport.hpp:336
std::optional< int64_t > lastPointFrame
Where the last grid point fell, on the same count.
Definition score-plugin-fx/Fx/Looper/Transport.hpp:293
LoopMode wanted
Definition score-plugin-fx/Fx/Looper/Transport.hpp:273
int64_t frame
Frames run since the transport was created, at the start of this tick.
Definition score-plugin-fx/Fx/Looper/Transport.hpp:291
void play_captured(int64_t position) noexcept
Definition score-plugin-fx/Fx/Looper/Transport.hpp:464
bool restart_pending() const noexcept
What waits for the next grid point besides a change of mode.
Definition score-plugin-fx/Fx/Looper/Transport.hpp:451
void settle(bool empty) noexcept
A loop emptied while Overdub is asked for, e.g. by Clear: it is recorded.
Definition score-plugin-fx/Fx/Looper/Transport.hpp:383
Definition LooperView.hpp:72
int take
Takes written so far; the files of the last one are take_files().
Definition LooperView.hpp:86