2 #include <Fx/Types.hpp>
4 #include <ossia/dataflow/exec_state_facade.hpp>
5 #include <ossia/dataflow/nodes/media.hpp>
6 #include <ossia/dataflow/token_request.hpp>
8 #include <halp/audio.hpp>
9 #include <halp/controls.hpp>
10 #include <halp/layout.hpp>
11 #include <halp/meta.hpp>
13 namespace Nodes::AudioLooper
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);
42 halp::dynamic_audio_bus<
"in",
double> audio;
43 halp::enum_t<LoopMode,
"Loop"> mode;
45 halp::toggle<
"Passthrough", halp::toggle_setup{.init =
true}> passthrough;
46 halp::enum_t<Postaction,
"Post-action"> postaction;
47 halp::spinbox_i32<
"Bars", halp::irange{0, 64, 4}> postaction_bars;
51 halp::mimic_audio_bus<
"out", &ins::audio> audio;
56 LoopMode quantizedPlayMode{LoopMode::Stop};
57 LoopMode actualMode{LoopMode::Stop};
58 ossia::audio_vector audio;
59 int64_t playbackPos{};
60 ossia::time_value recordStart{};
61 ossia::quarter_note recordStartBar{-1.};
62 ossia::quarter_note recordEndBar{-1.};
63 int actualChannels = 0;
65 std::optional<ossia::time_value> this_buffer_quantif_time;
66 std::optional<int64_t> this_buffer_quantif_sample;
67 int postaction_bars{};
68 double sampleRate{48000.};
69 bool isPostRecording{
false};
71 static constexpr int64_t default_buffer_size = 192000 * 32;
72 void reset_elapsed() { }
73 int channels()
const noexcept {
return actualChannels; }
74 void set_channels(
int chans)
76 const int64_t cur_channels = std::ssize(audio);
77 actualChannels = chans;
78 if(actualChannels > cur_channels)
80 audio.resize(actualChannels);
82 int64_t min_size = audio[0].size();
83 int64_t min_capa = std::max(int64_t(audio[0].capacity()), default_buffer_size);
84 for(
int i = cur_channels; i < actualChannels; i++)
86 audio[i].reserve(min_capa);
87 audio[i].resize(min_size);
90 else if(actualChannels < cur_channels)
92 for(
int i = actualChannels; i < cur_channels; i++)
102 for(
auto& vec : audio)
103 vec.reserve(default_buffer_size);
107 void fade(
const ossia::token_request& tk)
109 const double sr = state.sampleRate;
110 const double bar_samples
111 = sr * 4. * (double(tk.signature.upper) / tk.signature.lower) * (60. / tk.tempo);
112 const double total_samples = std::floor(state.postaction_bars * bar_samples);
115 const bool quantify_length = (state.quantif > 0.f) && (state.channels() > 0);
118 if(total_samples < state.audio[0].size())
120 for(
auto& a : state.audio)
121 a.resize(total_samples);
126 for(
auto& chan : state.audio)
128 int samples = chan.size();
129 if(
int min_n = std::min(samples, (
int)128); min_n > 0)
131 float f = 1. / min_n;
133 for(
int i = 0; i < min_n; i++)
139 for(
int i = samples - min_n; i < samples; i++)
150 if(total_samples > state.audio[0].size())
152 for(
auto& a : state.audio)
153 a.resize(total_samples);
158 void changeAction(
const ossia::token_request& tk)
160 if(state.quantizedPlayMode == LoopMode::Record)
162 state.recordStart = tk.prev_date;
163 state.recordStartBar = tk.musical_start_position;
164 state.recordEndBar = tk.musical_start_position
165 + (4. * double(tk.signature.upper) / tk.signature.lower)
166 * state.postaction_bars;
167 state.reset_elapsed();
171 state.recordStart = ossia::time_value{-1LL};
172 state.recordStartBar = -1.;
173 state.recordEndBar = -1.;
174 state.reset_elapsed();
180 void checkPostAction(
181 const std::string& postaction,
int postaction_bars,
const ossia::token_request& tk,
186 ossia::exec_state_facade ossia_state;
188 using tick = ossia::token_request;
189 void operator()(
const tick& tk)
191 using namespace ossia;
193 if(tk.date == tk.prev_date)
196 const LoopMode m = this->inputs.mode;
197 const int postaction_bars = this->inputs.postaction_bars;
198 const Postaction postaction = this->inputs.postaction;
199 const float quantif = this->inputs.quantif.value;
200 const bool passthrough = this->inputs.passthrough;
202 state.this_buffer_quantif_time = std::nullopt;
203 state.this_buffer_quantif_sample = std::nullopt;
204 state.postaction_bars = postaction_bars;
205 state.sampleRate = ossia_state.sampleRate();
207 if(quantif != 0 && tk.prev_date != 0_tv)
209 state.quantif = quantif;
210 if(
auto time = tk.get_quantification_date(1. / quantif);
211 time && time > tk.prev_date)
213 state.this_buffer_quantif_time = *time;
214 state.this_buffer_quantif_sample
215 = tk.to_physical_time_in_tick(*time, ossia_state.modelToSamples());
220 state.quantif = 0.0f;
223 if(m != state.quantizedPlayMode)
225 if(quantif != 0 && tk.prev_date != 0_tv)
227 if(
auto& time = state.this_buffer_quantif_time)
234 if(*time > tk.prev_date)
239 sub_tk.set_end_time(*time - 1_tv);
241 preAction(sub_tk, postaction, postaction_bars, passthrough);
245 state.quantizedPlayMode = m;
246 state.actualMode = m;
247 state.playbackPos = 0;
252 sub_tk.set_start_time(*time);
254 changeAction(sub_tk);
255 preAction(sub_tk, postaction, postaction_bars, passthrough);
261 state.quantizedPlayMode = m;
262 state.actualMode = m;
263 state.playbackPos = 0;
266 preAction(tk, postaction, postaction_bars, passthrough);
272 preAction(tk, postaction, postaction_bars, passthrough);
278 state.quantizedPlayMode = m;
279 state.actualMode = m;
280 state.playbackPos = 0;
283 preAction(tk, postaction, postaction_bars, passthrough);
289 preAction(tk, postaction, postaction_bars, passthrough);
294 ossia::token_request tk, Postaction postaction,
int postaction_bars,
297 using namespace ossia;
298 if(state.recordStartBar == -1.)
300 action(tk, passthrough);
306 auto switch_to_main_mode = [&] {
309 case Postaction::Play:
310 state.actualMode = LoopMode::Play;
312 case Postaction::Overdub:
313 state.actualMode = LoopMode::Overdub;
316 state.actualMode = LoopMode::Stop;
320 state.recordStart = ossia::time_value{-1};
321 state.recordStartBar = -1.;
322 state.reset_elapsed();
323 state.playbackPos = 0;
327 if(tk.musical_start_last_bar >= state.recordEndBar)
329 switch_to_main_mode();
331 action(tk, passthrough);
335 else if(tk.musical_end_last_bar >= state.recordEndBar)
337 if(
auto quant_date = tk.get_quantification_date(1.0))
339 ossia::time_value t = *quant_date;
345 sub_tk.set_end_time(t - 1_tv);
346 action(sub_tk, passthrough);
350 switch_to_main_mode();
356 sub_tk.set_start_time(t);
358 action(sub_tk, passthrough);
363 qDebug(
"very weird");
371 switch_to_main_mode();
377 action(tk, passthrough);
382 void action(
const ossia::token_request& tk,
bool echoRecord)
384 auto timings = ossia_state.timings(tk);
385 action(timings.start_sample, timings.length, echoRecord);
388 void action(int64_t start, int64_t length,
bool echoRecord)
390 switch(state.actualMode)
393 if(state.channels() == 0 || state.audio[0].size() == 0)
401 case LoopMode::Record:
402 echoRecord ? record(start, length) : record_noecho(start, length);
404 case LoopMode::Overdub:
410 void play(int64_t first_pos, int64_t samples)
412 auto& p2 = outputs.audio;
414 const auto chans = state.channels();
419 int64_t k = state.playbackPos;
420 for(
int i = 0; i < chans; i++)
422 auto& out = p2.samples[i];
423 auto& record = state.audio[i];
424 const int64_t chan_samples = record.size();
426 k = state.playbackPos;
427 if(state.playbackPos + samples < chan_samples)
429 for(int64_t j = first_pos; j < samples; j++)
437 int64_t max = chan_samples - state.playbackPos;
438 int64_t j = first_pos;
451 for(; j < std::min(samples, chan_samples); j++)
492 state.playbackPos = k;
496 void stop(int64_t first_pos, int64_t samples)
498 auto& p1 = inputs.audio;
499 auto& p2 = outputs.audio;
500 const auto chans = p1.channels;
502 for(
int i = 0; i < chans; i++)
504 auto& in = p1.samples[i];
505 auto& out = p2.samples[i];
507 for(int64_t j = first_pos; j < samples; j++)
514 void record(int64_t first_pos, int64_t samples)
516 auto& p1 = inputs.audio;
517 auto& p2 = outputs.audio;
519 const auto chans = p1.channels;
520 state.set_channels(chans);
522 for(
int i = 0; i < chans; i++)
524 auto& in = p1.samples[i];
525 auto& out = p2.samples[i];
526 auto& record = state.audio[i];
528 record.resize(state.playbackPos + samples);
529 int64_t k = state.playbackPos;
531 for(int64_t j = first_pos; j < samples; j++)
538 state.playbackPos += samples;
541 void record_noecho(int64_t first_pos, int64_t samples)
543 auto& p1 = inputs.audio;
545 const auto chans = p1.channels;
546 state.set_channels(chans);
548 for(
int i = 0; i < chans; i++)
550 auto& in = p1.samples[i];
551 auto& record = state.audio[i];
553 record.resize(state.playbackPos + samples);
554 int64_t k = state.playbackPos;
556 for(int64_t j = first_pos; j < samples; j++)
562 state.playbackPos += samples;
565 void overdub(int64_t first_pos, int64_t samples)
567 auto& p1 = inputs.audio;
568 auto& p2 = outputs.audio;
573 const auto chans = p1.channels;
574 state.set_channels(chans);
576 for(
int i = 0; i < chans; i++)
578 auto& in = p1.samples[i];
579 auto& out = p2.samples[i];
580 auto& record = state.audio[i];
581 const int64_t record_samples = record.size();
583 int64_t k = state.playbackPos;
585 for(int64_t j = first_pos; j < samples; j++)
587 if(k >= record_samples)
596 state.playbackPos += samples;
601 auto& p1 = inputs.audio;
602 auto& p2 = outputs.audio;
607 const auto chans = p1.channels;
608 state.set_channels(chans);
610 for(
int i = 0; i < chans; i++)
612 auto& in = p1.samples[i];
613 auto& out = p2.samples[i];
614 auto& record = state.audio[i];
615 const int64_t record_samples = record.size();
617 int64_t k = state.playbackPos;
619 for(int64_t j = first_pos; j < samples; j++)
621 if(k >= record_samples)
630 state.playbackPos += samples;
635 halp_meta(layout, halp::layouts::hbox)
638 halp_meta(layout, halp::layouts::vbox)
639 halp_meta(background, halp::colors::mid)
640 halp::control<&ins::mode> f;
641 halp::control<&ins::quantif> q;
642 halp::control<&ins::passthrough> p;
646 halp_meta(layout, halp::layouts::vbox)
647 halp_meta(background, halp::colors::mid)
648 halp::control<&ins::postaction> p;
649 halp::control<&ins::postaction_bars> b;
Utilities for OSSIA data structures.
Definition: DeviceInterface.hpp:33
Definition: Looper.hpp:41
Definition: Looper.hpp:634
Definition: Looper.hpp:16
void overdub_noecho(int64_t first_pos, int64_t samples)
Definition: Looper.hpp:599
void overdub(int64_t first_pos, int64_t samples)
Definition: Looper.hpp:565