17 halp_meta(name,
"Looper (audio)")
18 halp_meta(c_name,
"Looper (audio)")
19 halp_meta(category,
"Audio/Utilities")
20 halp_meta(author,
"ossia score")
21 halp_meta(manual_url,
"https://ossia.io/score-docs/processes/audio-looper.html")
22 halp_meta(description,
"Loop audio")
23 halp_meta(uuid,
"a0ad4227-ac3d-448b-a19b-19581ed4e2c6");
24 halp_meta(recommended_height, 65);
60 static clang_buggy_consteval
auto name() {
return std::string_view{
"Passthrough"}; }
67 decltype(avnd::enum_names<Passthrough>()) values = avnd::enum_names<Passthrough>();
71 operator Passthrough() const noexcept {
return value; }
81 halp::dynamic_audio_bus<
"in",
double> audio;
83 struct : halp::enum_t<LoopMode,
"Loop">
85 using halp::enum_t<LoopMode,
"Loop">::operator=;
88 "What the looper is doing: playing the loop, recording a new one over "
89 "it, overdubbing on top of it, or nothing.")
96 "Waits for the next point of this grid before changing mode, so a "
97 "loop starts and ends in time. Whole is a bar. None changes at once.")
102 using passthrough_selector::operator=;
105 "Whether the input is heard as well as the loop. Record passthrough "
106 "is heard only while recording or overdubbing; Full is heard always.")
109 struct : halp::enum_t<Postaction,
"Post-action">
111 using halp::enum_t<Postaction,
"Post-action">::operator=;
114 "What recording gives way to once it has run for Bars bars, without "
115 "the mode being touched.")
118 struct : halp::spinbox_i32<
"Bars", halp::irange{0, 64, 4}>
122 "How long a recording runs before the post-action takes over, and so "
123 "how long the loop it leaves is. Zero leaves recording to be ended by "
124 "hand, and the loop is then as long as it was played for.")
127 struct : halp::enum_t<
Restart,
"Restart"> {
128 using halp::enum_t<
Restart,
"Restart">::operator=;
131 "Where the loop is read from when the mode changes. Always reads it "
132 "from the beginning again; Recording lets playing and overdubbing "
133 "carry on from where they were.")
138 halp::mimic_audio_bus<
"out", &ins::audio> audio;
143 LoopMode quantizedPlayMode{LoopMode::Stop};
144 LoopMode actualMode{LoopMode::Stop};
145 ossia::audio_vector audio;
146 int64_t playbackPos{};
147 ossia::time_value recordStart{};
148 ossia::quarter_note recordStartBar{-1.};
149 ossia::quarter_note recordEndBar{-1.};
150 int actualChannels = 0;
152 std::optional<ossia::time_value> this_buffer_quantif_time;
153 std::optional<int64_t> this_buffer_quantif_sample;
154 int64_t tickStartSample{};
155 int postaction_bars{};
156 double sampleRate{48000.};
162 static constexpr int64_t default_buffer_size = 192000 * 32;
163 void reset_elapsed() { }
164 int channels() const noexcept {
return actualChannels; }
165 void set_channels(
int chans)
167 if(chans == actualChannels)
170 const int prev_channels = actualChannels;
171 actualChannels = chans;
172 if(std::ssize(audio) < chans)
177 const int64_t len = prev_channels > 0 ? std::ssize(audio[0]) : 0;
178 for(
int i = prev_channels; i < chans; i++)
180 if(std::ssize(audio[i]) != len)
182 audio[i].reserve(std::max(int64_t(audio[i].capacity()), default_buffer_size));
183 audio[i].resize(len);
191 for(
auto& vec : audio)
192 vec.reserve(default_buffer_size);
202 state.quantizedPlayMode = m;
203 state.actualMode = m;
205 state.playbackPos = 0;
208 void fade(
const ossia::token_request& tk)
216 const double sr = state.sampleRate;
217 const double bar_samples
218 = sr * 4. * (double(tk.signature.upper) / tk.signature.lower) * (60. / tk.tempo);
219 const double total_samples = std::floor(state.postaction_bars * bar_samples);
227 && (state.postaction_bars > 0)
228 && (state.channels() > 0);
231 if(total_samples < state.audio[0].size())
233 for(
auto& a : state.audio)
234 a.resize(total_samples);
239 for(
auto& chan : state.audio)
241 const int64_t samples = std::ssize(chan);
242 if(int64_t min_n = std::min(samples, int64_t(128)); min_n > 0)
244 float f = 1. / min_n;
246 for(int64_t i = 0; i < min_n; i++)
252 for(int64_t i = samples - min_n; i < samples; i++)
263 if(total_samples > state.audio[0].size())
265 for(
auto& a : state.audio)
266 a.resize(total_samples);
271 void changeAction(
const ossia::token_request& tk)
275 if(state.quantizedPlayMode == LoopMode::Record && state.postaction_bars > 0)
277 state.recordStart = tk.prev_date;
278 state.recordStartBar = tk.musical_start_position;
279 state.recordEndBar = tk.musical_start_position
280 + (4. * double(tk.signature.upper) / tk.signature.lower)
281 * state.postaction_bars;
283 state.reset_elapsed();
287 state.recordStart = ossia::time_value{-1LL};
288 state.recordStartBar = -1.;
289 state.recordEndBar = -1.;
290 state.reset_elapsed();
296 void checkPostAction(
297 const std::string& postaction,
int postaction_bars,
const ossia::token_request& tk,
302 ossia::exec_state_facade ossia_state;
304 using tick = ossia::token_request;
305 void operator()(
const tick& tk)
307 using namespace ossia;
309 if(tk.date == tk.prev_date)
312 const LoopMode m = this->inputs.mode;
313 const int postaction_bars = this->inputs.postaction_bars;
314 const Postaction postaction = this->inputs.postaction;
315 const float quantif = this->inputs.quantif.value;
316 const Passthrough passthrough = this->inputs.passthrough;
317 const Restart restart = this->inputs.restart;
319 state.this_buffer_quantif_time = std::nullopt;
320 state.this_buffer_quantif_sample = std::nullopt;
321 state.postaction_bars = postaction_bars;
322 state.sampleRate = ossia_state.sampleRate();
326 state.tickStartSample = ossia_state.timings(tk).start_sample;
328 if(quantif != 0 && tk.prev_date != 0_tv)
330 state.quantif = quantif;
335 if(
auto pt = tk.get_quantification_point(1. / quantif);
336 pt && pt->date >= tk.prev_date)
338 const double ratio = ossia_state.modelToSamples();
339 state.this_buffer_quantif_time = pt->date;
344 state.this_buffer_quantif_sample
345 = tk.physical_start(ratio) + tk.physical_position(pt->position, ratio);
350 state.quantif = 0.0f;
353 if(m != state.quantizedPlayMode)
355 if(quantif != 0 && tk.prev_date != 0_tv)
357 if(
auto& time = state.this_buffer_quantif_time)
364 if(*time > tk.prev_date)
371 sub_tk.set_end_time(*time);
373 preAction(sub_tk, postaction, postaction_bars, passthrough, restart);
382 sub_tk.set_start_time(*time);
384 changeAction(sub_tk);
385 preAction(sub_tk, postaction, postaction_bars, passthrough, restart);
394 preAction(tk, postaction, postaction_bars, passthrough, restart);
400 preAction(tk, postaction, postaction_bars, passthrough, restart);
409 preAction(tk, postaction, postaction_bars, passthrough, restart);
415 preAction(tk, postaction, postaction_bars, passthrough, restart);
420 ossia::token_request tk, Postaction postaction,
int postaction_bars,
423 using namespace ossia;
424 if(state.recordStartBar == -1.)
426 action(tk, passthrough);
432 auto switch_to_main_mode = [&] {
435 case Postaction::Play:
436 state.actualMode = LoopMode::Play;
438 case Postaction::Overdub:
439 state.actualMode = LoopMode::Overdub;
442 state.actualMode = LoopMode::Stop;
446 state.recordStart = ossia::time_value{-1};
447 state.recordStartBar = -1.;
449 state.reset_elapsed();
451 state.playbackPos = 0;
455 if(tk.musical_start_last_bar >= state.recordEndBar)
457 switch_to_main_mode();
459 action(tk, passthrough);
463 else if(tk.musical_end_last_bar >= state.recordEndBar)
465 const auto quant_date = tk.get_quantification_date(1.0);
466 if(quant_date && *quant_date > tk.prev_date && *quant_date < tk.date)
468 const ossia::time_value t = *quant_date;
473 sub_tk.set_end_time(t);
474 action(sub_tk, passthrough);
478 switch_to_main_mode();
484 sub_tk.set_start_time(t);
486 action(sub_tk, passthrough);
489 else if(tk.musical_end_position - tk.musical_end_last_bar < 1e-9)
496 action(tk, passthrough);
502 switch_to_main_mode();
504 action(tk, passthrough);
511 action(tk, passthrough);
516 void action(
const ossia::token_request& tk,
Passthrough pt)
518 auto timings = ossia_state.timings(tk);
519 action(timings.start_sample - state.tickStartSample, timings.length, pt);
522 void action(int64_t start, int64_t length,
Passthrough pt)
528 switch(state.actualMode)
531 if(state.channels() == 0 || state.audio[0].size() == 0)
532 echo_otherwise ? stop(start, length) :
silence(start, length);
537 mix_input(start, length);
541 echo_otherwise ? stop(start, length) :
silence(start, length);
543 case LoopMode::Record:
544 echo_while_taking_in ? record(start, length) : record_noecho(start, length);
546 case LoopMode::Overdub:
547 echo_while_taking_in ? overdub(start, length) : overdub_noecho(start, length);
552 void play(int64_t first_pos, int64_t samples)
554 auto& p2 = outputs.audio;
555 const int64_t last = first_pos + samples;
556 const int out_chans = p2.channels;
557 const int loop_chans = std::min(state.channels(), out_chans);
559 int64_t k = state.playbackPos;
560 for(
int i = 0; i < loop_chans; i++)
562 auto& out = p2.samples[i];
563 auto& record = state.audio[i];
564 const int64_t chan_samples = std::ssize(record);
566 if(chan_samples <= 0)
568 for(int64_t j = first_pos; j < last; j++)
575 k = state.playbackPos % chan_samples;
576 for(int64_t j = first_pos; j < last; j++)
579 if(++k == chan_samples)
585 for(
int i = loop_chans; i < out_chans; i++)
587 auto& out = p2.samples[i];
588 for(int64_t j = first_pos; j < last; j++)
592 state.playbackPos = k;
598 void silence(int64_t first_pos, int64_t samples)
600 auto& p2 = outputs.audio;
601 const int64_t last = first_pos + samples;
602 for(
int i = 0; i < p2.channels; i++)
604 auto& out = p2.samples[i];
605 for(int64_t j = first_pos; j < last; j++)
610 void mix_input(int64_t first_pos, int64_t samples)
612 auto& p1 = inputs.audio;
613 auto& p2 = outputs.audio;
614 const int chans = std::min(p1.channels, p2.channels);
615 const int64_t last = first_pos + samples;
616 for(
int i = 0; i < chans; i++)
618 auto& in = p1.samples[i];
619 auto& out = p2.samples[i];
620 for(int64_t j = first_pos; j < last; j++)
625 void stop(int64_t first_pos, int64_t samples)
627 auto& p1 = inputs.audio;
628 auto& p2 = outputs.audio;
629 const auto chans = p1.channels;
630 const int64_t last = first_pos + samples;
632 for(
int i = 0; i < chans; i++)
634 auto& in = p1.samples[i];
635 auto& out = p2.samples[i];
637 for(int64_t j = first_pos; j < last; j++)
644 void record(int64_t first_pos, int64_t samples)
646 auto& p1 = inputs.audio;
647 auto& p2 = outputs.audio;
649 const auto chans = p1.channels;
650 state.set_channels(chans);
651 const int64_t last = first_pos + samples;
653 for(
int i = 0; i < chans; i++)
655 auto& in = p1.samples[i];
656 auto& out = p2.samples[i];
657 auto& record = state.audio[i];
659 record.resize(state.playbackPos + samples);
660 int64_t k = state.playbackPos;
662 for(int64_t j = first_pos; j < last; j++)
669 state.playbackPos += samples;
673 void record_noecho(int64_t first_pos, int64_t samples)
675 auto& p1 = inputs.audio;
676 auto& p2 = outputs.audio;
678 const auto chans = p1.channels;
679 state.set_channels(chans);
680 const int64_t last = first_pos + samples;
682 for(
int i = 0; i < chans; i++)
684 auto& in = p1.samples[i];
685 auto& record = state.audio[i];
687 record.resize(state.playbackPos + samples);
688 int64_t k = state.playbackPos;
690 for(int64_t j = first_pos; j < last; j++)
697 for(
int i = 0; i < p2.channels; i++)
699 auto& out = p2.samples[i];
700 for(int64_t j = first_pos; j < last; j++)
704 state.playbackPos += samples;
708 void overdub(int64_t first_pos, int64_t samples)
710 auto& p1 = inputs.audio;
711 auto& p2 = outputs.audio;
714 const auto chans = p1.channels;
715 state.set_channels(chans);
716 const int64_t last = first_pos + samples;
718 int64_t k = state.playbackPos;
719 for(
int i = 0; i < chans; i++)
721 auto& in = p1.samples[i];
722 auto& out = p2.samples[i];
723 auto& record = state.audio[i];
724 const int64_t record_samples = std::ssize(record);
727 if(record_samples <= 0)
729 for(int64_t j = first_pos; j < last; j++)
734 k = state.playbackPos % record_samples;
735 for(int64_t j = first_pos; j < last; j++)
740 if(++k == record_samples)
744 state.playbackPos = k;
748 void overdub_noecho(int64_t first_pos, int64_t samples)
750 auto& p1 = inputs.audio;
751 auto& p2 = outputs.audio;
754 const auto chans = p1.channels;
755 state.set_channels(chans);
756 const int64_t last = first_pos + samples;
758 int64_t k = state.playbackPos;
759 for(
int i = 0; i < chans; i++)
761 auto& in = p1.samples[i];
762 auto& out = p2.samples[i];
763 auto& record = state.audio[i];
764 const int64_t record_samples = std::ssize(record);
766 if(record_samples <= 0)
768 for(int64_t j = first_pos; j < last; j++)
773 k = state.playbackPos % record_samples;
774 for(int64_t j = first_pos; j < last; j++)
779 if(++k == record_samples)
783 state.playbackPos = k;
789 halp_meta(layout, halp::layouts::hbox)
792 halp_meta(layout, halp::layouts::vbox)
793 halp_meta(background, halp::colors::background_mid)
794 halp::control<&ins::mode> f;
795 halp::control<&ins::quantif> q;
796 halp::control<&ins::passthrough> p;
800 halp_meta(layout, halp::layouts::vbox)
801 halp_meta(background, halp::colors::background_mid)
802 halp::control<&ins::postaction> p;
803 halp::control<&ins::postaction_bars> b;
804 halp::control<&ins::restart> r;