28class step_node final :
public ossia::nonowning_graph_node
31 ossia::value_inlet sequence_select;
32 ossia::value_inlet switch_quantification;
33 ossia::value_inlet duration;
34 ossia::value_outlet out;
40 int current_sequence{};
43 double switch_rate{1.};
58 int64_t
next_date{std::numeric_limits<int64_t>::min()};
62 m_inlets.push_back(&sequence_select);
63 m_inlets.push_back(&switch_quantification);
64 m_inlets.push_back(&duration);
65 m_outlets.push_back(&out);
68 std::string label() const noexcept
override {
return "Step"; }
70 void request_sequence(
int idx)
noexcept
74 idx = std::clamp(idx, 0,
int(
sequences.size()) - 1);
83 case ossia::val_type::FLOAT:
84 case ossia::val_type::INT:
85 case ossia::val_type::BOOL:
90 auto vec = ossia::convert<ossia::vec2f>(v);
92 synced = vec[1] != 0.f;
98 void read_controls() noexcept
100 if(
auto& d = sequence_select->get_data(); !d.empty())
101 request_sequence(ossia::convert<int>(d.back().value));
102 if(
auto& d = switch_quantification->get_data(); !d.empty())
103 switch_rate = ossia::convert<float>(d.back().value);
104 if(
auto& d = duration->get_data(); !d.empty())
108 void run(
const ossia::token_request& tk, ossia::exec_state_facade st)
noexcept override
115 current_sequence = std::clamp(current_sequence, 0,
int(
sequences.size()) - 1);
119 const double ratio = st.modelToSamples();
120 const int64_t tick_start = tk.physical_start(ratio);
121 const bool rewinding = tk.backward();
127 constexpr auto never = std::numeric_limits<int64_t>::max();
128 int64_t switch_at = never;
131 if(switch_rate <= 0.)
133 switch_at = tick_start;
137 const auto points = tk.get_quantification_dates(switch_rate);
139 switch_at = tick_start + tk.physical_position(points.front().position, ratio);
143 play_steps(tk, ratio, tick_start, rewinding, std::numeric_limits<int64_t>::min(), switch_at);
144 if(switch_at != never)
149 play_steps(tk, ratio, tick_start, rewinding, switch_at, never);
160 const auto& sig = tk.signature;
162 = (sig.upper > 0 && sig.lower > 0) ? 4. * sig.upper / sig.lower : 4.;
172 = tk.prev_date.impl / ossia::flicks_per_second<double> * tk.tempo / 60.;
173 origin = tk.musical_start_position - elapsed;
185 before = std::ceil((tk.musical_start_position -
origin) / q - 1e-9);
189 const double d =
step_duration * ossia::flicks_per_second<double>;
190 before = std::ceil(tk.prev_date.impl / d);
192 if(std::isfinite(before) && before >= 0.)
193 current = int(std::fmod(before,
double(n)));
198 const ossia::token_request& tk,
double ratio, int64_t tick_start,
bool rewinding,
199 int64_t from, int64_t to)
noexcept
204 auto play = [&](int64_t sample) {
205 if(sample >= from && sample < to)
206 play_step(sample, rewinding);
214 const double ms = tk.musical_start_position;
215 const double me = tk.musical_end_position;
216 if(!(q > 0.) || ms == me)
218 constexpr double eps = 1e-9;
219 int64_t k = rewinding ? int64_t(std::floor((ms -
origin) / q + eps))
220 : int64_t(std::ceil((ms -
origin) / q - eps));
222 for(
int guard = 0; guard < 4096; guard++, k += rewinding ? -1 : 1)
224 const double m =
origin + k * q;
225 if(rewinding ? (m <= me + eps) : (m >= me - eps))
227 play(tick_start + tk.physical_position(m, ratio));
233 const double d =
step_duration * ossia::flicks_per_second<double>;
234 const int64_t lo = std::min(tk.prev_date.impl, tk.date.impl);
235 const int64_t hi = std::max(tk.prev_date.impl, tk.date.impl);
238 int64_t k0 = rewinding ? int64_t(std::floor(lo / d)) + 1 : int64_t(std::ceil(lo / d));
239 int64_t k1 = rewinding ? int64_t(std::floor(hi / d)) : int64_t(std::ceil(hi / d)) - 1;
241 k1 = std::min(k1, k0 + 4096);
244 for(int64_t k = k0; k <= k1; k++)
245 play(tk.to_physical_time_in_tick(ossia::time_value{int64_t(k * d)}, ratio));
249 for(int64_t k = k1; k >= k0; k--)
250 play(tk.to_physical_time_in_tick(ossia::time_value{int64_t(k * d)}, ratio));
255 void play_step(int64_t sample,
bool rewinding)
noexcept
257 const auto& seq =
sequences[current_sequence];
258 const int n = seq.size();
265 if(current < 0 || current >= n)
267 out->write_value(seq[
current], sample);