2#include <Fx/Looper/Transport.hpp>
4#include <ossia/network/value/value.hpp>
5#include <ossia/network/value/value_conversion.hpp>
7#include <halp/controls.hpp>
8#include <halp/grid_chooser.hpp>
9#include <halp/layout.hpp>
10#include <halp/meta.hpp>
11#include <halp/static_string.hpp>
13#include <avnd/common/enum_reflection.hpp>
45template <auto F,
int Columns>
48 halp_meta(layout, halp::layouts::control)
49 static constexpr int columns()
noexcept {
return Columns; }
50 decltype(F) model = F;
54template <halp::static_
string Name,
typename Enum, Enum Init>
57 static clang_buggy_consteval
auto name() {
return std::string_view{Name.value}; }
64 decltype(avnd::enum_names<Enum>()) values = avnd::enum_names<Enum>();
68 operator Enum() const noexcept {
return value; }
69 auto& operator=(Enum t)
noexcept
82 static constexpr std::array<std::string_view, 4> names{
83 "Play",
"Record",
"Overdub",
"Stop"};
84 const auto i = std::size_t(m);
85 return i < names.size() ? names[i] : names[3];
89template <
typename Field,
typename V>
93 if(f.update_controller)
94 f.update_controller(v);
97template <
typename Field>
98void show_mode(Field& f, LoopMode m)
101 if(f.update_controller)
125 LoopMode shown{LoopMode::Stop};
128template <
typename Field>
129void read_loop(Field& f, transport& t, loop_input& l,
bool empty)
131 const bool arrived = std::exchange(l.arrived,
false) || f.value != l.left;
134 const LoopMode v = f.value;
135 const bool repeated = l.streamed && v == l.last_in;
136 if(!repeated && v != t.wanted)
140 l.streamed = arrived;
144template <
typename Field>
150 show_mode(f, l.shown);
159 static clang_buggy_consteval
auto name() {
return std::string_view{
"After record"}; }
160 static constexpr std::string_view former_name()
noexcept {
return "Post-action"; }
169 std::string_view values[2] = {
"Play",
"Overdub"};
173 operator AfterRecord() const noexcept {
return value; }
181 "What a recording gives way to when it ends: when its bars run out, "
182 "when the buffer is full, or by Rec / Play / Dub. Play plays the loop; "
183 "Overdub overdubs it from the very frame the recording ends on, so that "
184 "what is held goes on without a gap. Setting Mode ends a recording into "
185 "the mode it is set to.")
192 static clang_buggy_consteval
auto name() {
return std::string_view{
"Start from"}; }
193 static constexpr std::string_view former_name()
noexcept {
return "Restart"; }
194 static constexpr std::array<std::string_view, 3> former_values()
noexcept
196 return {
"Always",
"Recording",
"In phase"};
206 std::string_view values[3] = {
"Loop start",
"Where it was",
"In phase"};
210 operator StartFrom() const noexcept {
return value; }
218 "Where the loop is read from when the mode changes. Loop start reads "
219 "it from its beginning again; Where it was lets playing and overdubbing "
220 "carry on from where the playhead is, so that Play, Overdub and Play "
221 "again stay in time. In phase carries on as well, and a loop played "
222 "again after a stop, or when the score starts or jumps, is where it "
223 "would be had it kept playing with the timeline. A recording always "
224 "starts at the loop's start.")
230 using grid_chooser::operator=;
231 static constexpr std::string_view former_name()
noexcept {
return "Quantif"; }
238 const auto t = old.get_type();
239 if(t != ossia::val_type::FLOAT && t != ossia::val_type::INT
240 && t != ossia::val_type::BOOL)
242 const halp::grid_value g = legacy_grid(ossia::convert<float>(old));
243 return ossia::vec2f{float(g.value), float(
int(g.unit))};
245 static constexpr halp::grid_value legacy_grid(
float whole_note_fraction)
noexcept
247 if(!(whole_note_fraction > 0.f))
249 if(whole_note_fraction > 1.f - 1e-6f && whole_note_fraction < 1.f + 1e-6f)
250 return halp::grid_value::bars(1.);
251 return halp::grid_value::note(whole_note_fraction);
256 "Waits for the next point of this grid before changing mode, so a loop "
257 "starts and ends in time: a time, a note value (1/1 is a whole note, "
258 "four quarters), a number of bars, or the loop itself. Loop waits for "
259 "the loop to come round to its start; before there is a loop, for the "
260 "length the recording will have, Record bars bars (one bar when Record "
261 "bars is 0). None changes at once.")
266 .units = halp::grid_units::note, .init = halp::grid_value::note(0.25)};
272 using grid_chooser::operator=;
273 static constexpr std::string_view former_name()
noexcept {
return "Grid"; }
280 const auto t = old.get_type();
281 if(t != ossia::val_type::FLOAT && t != ossia::val_type::INT
282 && t != ossia::val_type::BOOL)
284 const float f = ossia::convert<float>(old);
285 const double v = f > 0.f ? f :
note_grid.init.value;
286 return ossia::vec2f{float(v), float(
int(halp::grid_unit::note))};
291 "The note value the notes are pulled towards when played back, as far "
296 :
enum_combobox<"At max layers", AtMaxLayers, AtMaxLayers::MergeIntoNewest>
298 using enum_combobox::operator=;
301 std::string_view values[3]
302 = {
"Merge oldest",
"Merge into newest",
"Replace a muted layer"};
307 "What a pass does when there are Max layers already. Merge oldest "
308 "merges the two oldest layers into one, losing their undo. Merge into "
309 "newest merges the pass into the newest layer once it ends: undo still "
310 "takes it out, until the next pass starts. Replace a muted layer drops "
311 "the oldest muted layer for the pass, else the newest layer.")
318 "One button for the whole loop: records an empty loop, ends the recording "
319 "into what After record says, overdubs a playing loop, and does what "
320 "After overdub says while overdubbing. A stopped loop plays. Pressed "
321 "again before the grid, each press is taken up on a grid point of its "
327 using enum_combobox::operator=;
330 std::string_view values[3] = {
"Grid",
"Loop end",
"Now"};
335 "When a stop is taken up, from Stop, Play / Stop or Mode: on the next "
336 "point of the grid, once the loop has played to its end, or at once.")
340 :
enum_combobox<"After overdub", AfterOverdub, after_overdub_default>
342 using enum_combobox::operator=;
345 std::string_view values[2] = {
"Play",
"New layer"};
350 "What Rec / Play / Dub does while overdubbing: go back to playing, or "
351 "go on overdubbing in a new layer, as New layer does.")
356 halp_meta(description,
"Plays a stopped loop; stops it in any other mode.")
363 "Sets Mode to Record: records an empty loop, overdubs one that has "
364 "something in it. Clear the loop first to record a new one. Retrigger "
365 "starts a recording again.")
372 "Sets Mode to Overdub. On an empty loop, records it, and the recording "
373 "then gives way to overdubbing.")
378 halp_meta(description,
"Sets Mode to Play.")
383 halp_meta(description,
"Sets Mode to Stop.")
390 "While overdubbing, ends the layer on the grid and goes on overdubbing "
391 "in a new one, without the playhead moving. Does nothing otherwise.")
398 "Goes back to the start of the loop on the grid, in the same mode: an "
399 "overdub goes on from there in the same layer. While recording, starts "
400 "the recording again, its bars and tempo counted from there.")
407 "Selects the layer before the one Layer says, which Mute layer, Erase "
408 "layer and Solo layer act on. The view outlines it.")
413 halp_meta(description,
"Selects the layer after the one Layer says.")
420 "Mutes the layer Layer says, or unmutes it, on the grid, as the m of "
421 "its lane does. A muted layer is kept but not heard: it takes nothing "
422 "from the layers under it, is not in the mix of a take, and still has "
423 "its file in a take written as layers.")
430 "Removes the layer Layer says, on the grid, as the x of its lane does. "
431 "The layer being overdubbed is dropped and the overdub goes on in a new "
432 "one, as with Undo.")
439 "Mutes every layer but the one Layer says, on the grid. Pressed again "
440 "on that layer, the layers get back the mutes they had, unless a mute "
441 "or an erase changed them since; on another layer, solos that one.")
446 halp_meta(description,
"Turns Reverse on, or off, on the grid.")
451 halp_meta(description,
"Sets Speed to x1/2, or back to x1 when it is x1/2, on the grid.")
456 halp_meta(description,
"Sets Speed to x2, or back to x1 when it is x2, on the grid.")
462 static constexpr int max_passes = 256;
463 std::array<bool, max_passes> muted{};
468 void forget() noexcept {
count = -1; }
471 template <
typename Engine>
476 for(
int i = 0; i <
count; i++)
477 if(e.looper_muted(i) != (i != pass))
483 template <
typename Engine>
487 if(soloed && selected == pass)
489 for(
int i = 0; i <
count; i++)
490 e.looper_set_muted(i, muted[i]);
494 const int n = std::min(e.looper_passes(), max_passes);
495 if(selected < 0 || selected >= n)
500 for(
int i = 0; i < n; i++)
501 muted[i] = e.looper_muted(i);
502 for(
int i = 0; i < n; i++)
503 e.looper_set_muted(i, i != selected);
513 enum kind_t : uint8_t
530 static constexpr int max_actions = 16;
531 std::array<layer_action, max_actions> actions{};
534 std::optional<float> speed;
539 void reset() noexcept
546 void add(layer_action a, transport& t)
noexcept
548 if(action_count < max_actions)
549 actions[action_count++] = a;
555 template <
typename Inputs,
typename Engine>
559 t.
press(a, empty, in.after_record.value, in.after_overdub.value);
561 const auto set_loop = [&](LoopMode m) {
565 if(in.rec_play_dub.value)
567 if(in.play_stop.value)
570 set_loop(LoopMode::Play);
572 set_loop(LoopMode::Stop);
574 set_loop(LoopMode::Record);
576 set_loop(LoopMode::Overdub);
577 if(in.new_layer.value)
579 if(in.retrigger.value)
582 if(
const int n = e.looper_passes();
583 n > 0 && (in.next_layer.value || in.previous_layer.value))
585 const int step = (in.next_layer.value ? 1 : 0) - (in.previous_layer.value ? 1 : 0);
586 show_control(in.layer, std::clamp(std::clamp(in.layer.value, 1, n) + step, 1, n));
589 if(in.mute_layer.value)
590 add({layer_action::mute, -1, -1}, t);
591 if(in.erase_layer.value)
592 add({layer_action::erase, -1, -1}, t);
593 if(in.solo_layer.value)
594 add({layer_action::solo, -1, -1}, t);
597 if(in.reverse_toggle.value)
598 reverse = !reverse, asked =
true;
600 const auto toggle_speed = [&](
float to) {
601 const float from = speed ? *speed : in.speed.value;
602 speed = from == to ? 1.f : to;
605 if(in.half_speed.value)
607 if(in.double_speed.value)
614 template <
typename Inputs,
typename Engine>
617 for(
int i = 0; i < action_count; i++)
619 const auto& a = actions[i];
620 const int n = e.looper_passes();
621 if(a.layers >= 0 && a.layers != n)
624 const int pass = a.pass >= 0 ? a.pass : in.layer.value - 1;
627 case layer_action::mute:
630 case layer_action::erase:
631 e.looper_erase(pass);
633 case layer_action::solo:
637 follow(in, e.looper_passes());
640 if(std::exchange(reverse,
false))
643 show_control(in.speed, *std::exchange(speed, std::nullopt));
648 template <
typename Inputs>
651 const int was = std::exchange(
seen_passes, passes);
654 else if(passes > 0 && in.layer.value > passes)
659 template <
typename Inputs>
663 if(pass < in.layer.value - 1 && in.layer.value > 1)
670 template <
typename Inputs>
674 if(in.layer.value - 1 > at && in.layer.value > 1)
680 template <
typename Inputs>
683 if(pass <= in.layer.value - 1 && in.layer.value < passes)
690 template <
typename Inputs,
typename Log>
695 for(
int i = 0; i < log.count; i++)
704template <
typename Ins>
707 halp_meta(name,
"Switches")
708 halp_meta(layout, halp::layouts::hbox)
709 halp_meta(alignment, halp::alignments::left)
710 halp_meta(spacing, 6)
713 halp_meta(name,
"One button")
714 halp_meta(layout, halp::layouts::section)
715 halp_meta(alignment, halp::alignments::left)
716 halp_meta(spacing, 6)
719 halp_meta(layout, halp::layouts::grid)
720 halp_meta(alignment, halp::alignments::left)
721 halp_meta(spacing, 6)
722 halp_meta(columns, 2)
723 halp::item<&Ins::rec_play_dub> rec_play_dub;
724 halp::item<&Ins::play_stop> play_stop;
725 halp::item<&Ins::after_record> after_record;
726 halp::item<&Ins::after_overdub> after_overdub;
727 halp::item<&Ins::stop_at> stop_at;
732 halp_meta(name,
"Transport")
733 halp_meta(layout, halp::layouts::section)
734 halp_meta(alignment, halp::alignments::left)
735 halp_meta(spacing, 6)
738 halp_meta(layout, halp::layouts::grid)
739 halp_meta(alignment, halp::alignments::left)
740 halp_meta(spacing, 6)
741 halp_meta(columns, 2)
742 halp::item<&Ins::play> play;
743 halp::item<&Ins::stop> stop;
744 halp::item<&Ins::record> record;
745 halp::item<&Ins::overdub> overdub;
746 halp::item<&Ins::new_layer> new_layer;
747 halp::item<&Ins::retrigger> retrigger;
754template <
typename Ins>
757 halp_meta(name,
"Layers")
758 halp_meta(layout, halp::layouts::grid)
759 halp_meta(alignment, halp::alignments::left)
760 halp_meta(spacing, 6)
761 halp_meta(columns, 3)
762 halp::item<&Ins::undo> undo;
763 halp::item<&Ins::redo> redo;
764 halp::item<&Ins::clear> clear;
765 halp::item<&Ins::layer> layer;
766 halp::item<&Ins::previous_layer> previous_layer;
767 halp::item<&Ins::next_layer> next_layer;
768 halp::item<&Ins::mute_layer> mute_layer;
769 halp::item<&Ins::erase_layer> erase_layer;
770 halp::item<&Ins::solo_layer> solo_layer;
776template <
typename Ins>
779 halp_meta(name,
"Speed")
780 halp_meta(layout, halp::layouts::vbox)
781 halp_meta(alignment, halp::alignments::left)
782 halp_meta(spacing, 6)
785 halp_meta(layout, halp::layouts::grid)
786 halp_meta(alignment, halp::alignments::left)
787 halp_meta(spacing, 6)
788 halp_meta(columns, 3)
789 halp::item<&Ins::speed> speed;
790 halp::item<&Ins::reverse> reverse;
791 halp::item<&Ins::follow_tempo> follow_tempo;
795 halp_meta(layout, halp::layouts::grid)
796 halp_meta(alignment, halp::alignments::left)
797 halp_meta(spacing, 6)
798 halp_meta(columns, 3)
799 halp::item<&Ins::reverse_toggle> reverse;
800 halp::item<&Ins::half_speed> half_speed;
801 halp::item<&Ins::double_speed> double_speed;
Link of score with the OSSIA API execution engine.
Definition CurveConversion.hpp:8
Definition AudioLayers.hpp:14
AfterRecord
Definition score-plugin-fx/Fx/Looper/Transport.hpp:86
void show_control(Field &f, V v)
Sets a control from the audio thread, and shows it in the UI.
Definition Switches.hpp:90
std::string_view mode_label(LoopMode m) noexcept
The name Mode shows a mode by.
Definition Switches.hpp:80
StopAt
When a stop is taken up.
Definition score-plugin-fx/Fx/Looper/Transport.hpp:93
@ Grid
On the next point of the grid, as any change of mode.
AtMaxLayers
What a pass that would make more layers than Max layers does.
Definition score-plugin-fx/Fx/Looper/Transport.hpp:138
@ MergeIntoNewest
The pass is merged into the newest layer once it ends.
constexpr AfterOverdub after_overdub_default
What Rec / Play / Dub does while overdubbing until the setting is changed.
Definition Switches.hpp:77
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
TransportAction
Definition score-plugin-fx/Fx/Looper/Transport.hpp:109
@ Retrigger
Back to the loop's start, in the same mode.
@ NewLayer
While overdubbing, ends the layer and starts another.
@ PlayStop
Plays a stopped loop, stops anything else.
@ RecPlayDub
Records an empty loop, then plays, overdubs, plays again.
StartFrom
Definition score-plugin-fx/Fx/Looper/Transport.hpp:121
@ WhereItWas
Only a recording does; playing and overdubbing carry straight on.
constexpr halp::grid_setup note_grid
The note values the MIDI looper's notes are pulled to.
Definition Switches.hpp:265
AfterOverdub
What Rec / Play / Dub does while overdubbing.
Definition score-plugin-fx/Fx/Looper/Transport.hpp:101
Definition Switches.hpp:344
Definition Switches.hpp:341
Definition Switches.hpp:168
Definition Switches.hpp:158
Definition Switches.hpp:300
Definition Switches.hpp:297
Definition Switches.hpp:63
An enumeration shown as a combobox: compact where the choices are many.
Definition Switches.hpp:56
Definition Switches.hpp:47
Definition Switches.hpp:512
Definition Switches.hpp:756
Definition Switches.hpp:271
static ossia::value upgrade_value(const ossia::value &old)
Definition Switches.hpp:278
The grid a change of mode waits for. The Quantif of older scores.
Definition Switches.hpp:229
static ossia::value upgrade_value(const ossia::value &old)
Definition Switches.hpp:236
The mutes the layers had before a solo, to give them back.
Definition Switches.hpp:461
bool still_soloed(const Engine &e) const noexcept
The layers are muted as the solo left them.
Definition Switches.hpp:472
int count
Passes when the solo was made, -1 when there is none to undo.
Definition Switches.hpp:465
void toggle(Engine &e, int selected)
Solos selected, or unsolos it when it is the layer soloed.
Definition Switches.hpp:484
Definition Switches.hpp:778
Definition Switches.hpp:205
Definition Switches.hpp:191
Definition Switches.hpp:329
Definition Switches.hpp:326
Definition Switches.hpp:706
Definition Switches.hpp:529
int seen_passes
Passes when Layer was last made to follow them.
Definition Switches.hpp:537
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
void merged(Inputs &in, int at)
Definition Switches.hpp:671
void follow(Inputs &in, int passes)
Definition Switches.hpp:649
void read(Inputs &in, transport &t, Engine &e, bool empty)
Definition Switches.hpp:556
void removed(Inputs &in, int pass)
Pass pass went from the layers: the one Layer says keeps its place.
Definition Switches.hpp:660
void inserted(Inputs &in, int pass, int passes)
Definition Switches.hpp:681
Definition score-plugin-fx/Fx/Looper/Transport.hpp:269
void ask(LoopMode m, bool empty) noexcept
Definition score-plugin-fx/Fx/Looper/Transport.hpp:366
bool queued() const noexcept
Changes queued after the one waiting for the grid.
Definition score-plugin-fx/Fx/Looper/Transport.hpp:448
LoopMode wanted
Definition score-plugin-fx/Fx/Looper/Transport.hpp:273
void request_point() noexcept
Definition score-plugin-fx/Fx/Looper/Transport.hpp:445
void press(TransportAction a, bool empty, AfterRecord after_record, AfterOverdub after) noexcept
Definition score-plugin-fx/Fx/Looper/Transport.hpp:394