Etterna 0.74.4
Loading...
Searching...
No Matches
SequencedBaseDiffCalc.h
1#pragma once
2#include "SequencingHelpers.h"
3#include <array>
4#include <unordered_map>
5
6/* MS difficulty bases are going to be sequence constructed row by row here, the
7 * nps base may be moved here later but not right now. we'll use statically
8 * allocated arrays to built difficulty for each interval, and the output will
9 * be placed into statically allocated arrays for base difficulties. (in
10 * threaded calc object now, though) */
11
13constexpr float min_threshold = 0.65F;
14static const float downscale_logbase = std::log(6.2F);
15
16constexpr float scaler_for_ms_base = 1.175F;
17// i do not know a proper name for these
18constexpr float ms_base_finger_weighter_2 = 9.F;
19constexpr float ms_base_finger_weighter = 5.5F;
20
21static auto
22CalcMSEstimate(std::vector<float>& input, const int& burp) -> float
23{
24 // how many ms values we use from here, if there are fewer than this
25 // number we'll mock up some values to water down intervals with a
26 // single extremely fast minijack, if there are more, we will truncate
27 unsigned int num_used = burp;
28
29 if (input.empty()) {
30 return 0.F;
31 }
32
33 // avoiding this for now because of smoothing
34 // single ms value, dunno if we want to do this? technically the tail
35 // end of an insanely hard burst that gets lopped off at the last note
36 // is still hard? if (input.size() < 2) return 1.f;
37
38 // sort before truncating/filling
39 std::sort(input.begin(), input.end());
40
41 // truncate if we have more values than what we care to sample, we're
42 // looking for a good estimate of the hardest part of this interval
43 // if above 1 and below used_ms_vals, fill up the stuff with dummies
44 // my god i was literally an idiot for doing what i was doing before
45 static const float ms_dummy = 360.F;
46
47 // mostly try to push down stuff like jumpjacks, not necessarily to push
48 // up "complex" stuff (this will push up intervals with few fast ms
49 // values kinda hard but it shouldn't matter as their base ms diff
50 // should be extremely low
51 float cv_yo = cv_trunc_fill(input, burp, ms_dummy) + 0.5F;
52 cv_yo = std::clamp(cv_yo, 0.5F, 1.25F);
53
54 // basically doing a jank average, bigger m = lower difficulty
55 float m = sum_trunc_fill(input, burp, ms_dummy);
56
57 // add 1 to num_used because some meme about sampling
58 // same thing as jack stuff, convert to bpm and then nps
59 float bpm_est = ms_to_bpm(m / (num_used + 1));
60 float nps_est = bpm_est / 15.F;
61 float fdiff = nps_est * cv_yo;
62 return fdiff;
63}
64
65struct nps
66{
68 static void actual_cancer(Calc& calc, const int& hand)
69 {
70 auto scaly_ms_estimate = [](std::vector<float>& input,
71 const float& scaler) {
72 float o = CalcMSEstimate(input, 3);
73 if (input.size() > 3) {
74 o = std::max(o, CalcMSEstimate(input, 4) * scaler);
75 }
76 if (input.size() > 4) {
77 o = std::max(o, CalcMSEstimate(input, 5) * scaler * scaler);
78 }
79 return o;
80 };
81
82 // Per-finger data for msbase
83 struct FingerGaps
84 {
85 std::vector<float> ms;
86 float last_row_time = s_init;
87 };
88
89 std::vector<FingerGaps> fingy(calc.col_masks.size());
90 std::vector<float> estimates;
91
92 for (auto itv = 0; itv < calc.numitv; ++itv) {
93 auto notes = 0;
94
95 for (auto& fing : fingy) {
96 fing.ms.clear();
97 }
98
99 // note time deltas per finger in this interval
100 for (auto row = 0; row < calc.itv_size.at(itv); ++row) {
101 const auto& cur = calc.adj_ni.at(itv).at(row);
102 auto cur_notes = cur.row_notes & calc.hand_col_masks[hand];
103 notes += cur.hand_counts.at(hand);
104
105 const auto& crt = cur.row_time;
106 for (size_t col_index = 0; col_index < calc.col_masks.size(); ++col_index) {
107 if (cur_notes & calc.col_masks[col_index]) {
108 auto& fing = fingy[col_index];
109 if (fing.last_row_time != s_init) {
110 const auto ms = ms_from(crt, fing.last_row_time);
111 fing.ms.push_back(ms);
112 }
113 fing.last_row_time = crt;
114 }
115 }
116 }
117
118 // per finger deltas to ms estimates. for 4k estimates.size() == 2
119 estimates.clear();
120 for (auto& fing : fingy) {
121 if (fing.last_row_time != s_init) {
122 auto ms_est = scaly_ms_estimate(fing.ms, scaler_for_ms_base);
123 estimates.push_back(ms_est);
124 }
125 }
126
127 // sort largest first and zero pad to hand note count
128 std::sort(estimates.begin(), estimates.end(), [](auto a, auto b) { return a > b; });
129 estimates.resize(column_count(calc.hand_col_masks[hand]));
130
131 // average fingy estimates for msbase. makes sense for 4k where there are 2 estimates.
132 // weighted_average fudges the 1/2 factor so generalising the 4k weights to nk is ??
133 // so just telescope
134 float msdiff = estimates[0];
135 for (size_t i = 1; i < estimates.size(); ++i) {
136 msdiff = weighted_average(msdiff, estimates[i], ms_base_finger_weighter, ms_base_finger_weighter_2);
137 }
138
139 // nps for this interval
140 const auto nps = static_cast<float>(notes) * finalscaler * 1.6F;
141 calc.init_base_diff_vals.at(hand).at(NPSBase).at(itv) = nps;
142
143 // ms base for this interval
144 const auto msbase = finalscaler * msdiff;
145 calc.init_base_diff_vals.at(hand).at(MSBase).at(itv) = msbase;
146
147 // set points for this interval
148 calc.itv_points.at(hand).at(itv) = notes * 2;
149 }
150 }
151
153 static void grindscale(Calc& calc) {
154 auto populated_intervals = 0;
155 auto avg_notes = 0.F;
156 for (auto itv = 0; itv < calc.numitv; ++itv) {
157 auto notes = 0.F;
158
159 for (auto& hand : both_hands) {
160 notes += calc.init_base_diff_vals.at(hand).at(NPSBase).at(itv);
161 }
162
163 if (notes > 0) {
164 avg_notes += notes;
165 populated_intervals++;
166 }
167 }
168
169 if (populated_intervals > 0) {
170 // const auto empty_intervals = static_cast<float>(calc.numitv - populated_intervals);
171 avg_notes /= populated_intervals;
172
173 auto failed_intervals = 0;
174
175 for (auto itv = 0; itv < calc.numitv; itv++) {
176 auto notes = 0.F;
177 for (auto& hand : both_hands)
178 notes +=
179 calc.init_base_diff_vals.at(hand).at(NPSBase).at(itv);
180
181 // count only intervals with notes
182 if (notes > 0.F && notes < avg_notes * min_threshold)
183 failed_intervals++;
184 }
185
186 // base grindscaler on how many intervals are passing
187 // if the entire file is just single taps or empty:
188 // ask yourself... is it really worth playing?
189 const auto file_length =
190 itv_idx_to_time(populated_intervals - failed_intervals);
191 // log minimum but if you move this you need to move the log base
192 const auto ping = .3F;
193 const auto timescaler =
194 (ping * (std::log(file_length + 1) / downscale_logbase)) + ping;
195
196 calc.grindscaler = std::clamp(timescaler, .1F, 1.F);
197 } else {
198 calc.grindscaler = .1F;
199 }
200 }
201};
202
203struct ceejay
204{
205 const std::string name = "CJ_Static";
206
207#pragma region params
208
209 float static_ms_weight = 0.65F;
210 float min_ms = 75.F;
211
212 float base_tap_scaler = 1.2F;
213 float huge_anchor_scaler = 1.15F;
214 float small_anchor_scaler = 1.15F;
215 float ccj_scaler = 1.25F;
216 float cct_scaler = 1.5F;
217 float ccn_scaler = 1.15F;
218 float ccb_scaler = 1.25F;
219
220 int mediterranean = 10;
221
222 const std::vector<std::pair<std::string, float*>> _params{
223 { "static_ms_weight", &static_ms_weight },
224 { "min_ms", &min_ms },
225 { "base_tap_scaler", &base_tap_scaler },
226 { "huge_anchor_scaler", &huge_anchor_scaler },
227 { "small_anchor_scaler", &small_anchor_scaler },
228 { "chord-chord-jack_scaler", &ccj_scaler },
229 { "chord-chord-tap_scaler", &cct_scaler },
230 { "chord-chord-no-anchors_scaler", &ccn_scaler },
231 { "chordjacks_beginning_scaler", &ccb_scaler },
232 };
233
234#pragma endregion params and param map
235
236
237 void update_flags(const unsigned& row_notes, const int& row_count)
238 {
239 is_cj = last_row_count > 1 && row_count > 1;
240 was_cj = last_row_count > 1 && last_last_row_count > 1;
241
242 is_scj = (row_count == 1 && last_row_count > 1) &&
243 ((row_notes & last_row_notes) != 0u);
244
245 is_actually_continuing_jack = ((row_notes & last_row_notes) != 0u);
246
247 carpathian_basin_capsized_boat_chord_bonk.push_back(row_notes ==
248 last_row_notes);
249
250 if (carpathian_basin_capsized_boat_chord_bonk.size() > mediterranean) {
251 carpathian_basin_capsized_boat_chord_bonk.erase(
252 carpathian_basin_capsized_boat_chord_bonk.begin());
253 }
254
255 is_at_least_3_note_anch =
256 ((row_notes & last_row_notes) & last_last_row_notes) != 0u;
257
258 last_last_row_count = last_row_count;
259 last_row_count = row_count;
260
261 last_last_row_notes = last_row_notes;
262 last_row_notes = row_notes;
263
264 last_was_3_note_anch = is_at_least_3_note_anch;
265
266 if (is_actually_continuing_jack) {
267 chain++;
268 } else {
269 chain = 1;
270 }
271 }
272
273 void advance_base(const float& any_ms, Calc& calc)
274 {
275 if (row_counter >= max_rows_for_single_interval) {
276 return;
277 }
278
279 // pushing back ms values, so multiply to nerf
280 float pewpew = base_tap_scaler;
281
282 if (chain < 3)
283 pewpew *= 1.1F; // stupid and gay
284 if (chain == 3)
285 pewpew /= 1.1F; // yes im really doing this
286
287 int laguardiaairport = 0;
288 for (int i = 0; i < carpathian_basin_capsized_boat_chord_bonk.size();
289 i++) {
290 laguardiaairport += carpathian_basin_capsized_boat_chord_bonk[i];
291 }
292
293 pewpew *= std::pow(1.025F, laguardiaairport);
294
295 // single note streams / regular jacks should retain the base
296 // multiplier
297 // cry about it
298
299 const auto ms = std::max(min_ms, any_ms * pewpew);
300 calc.cj_static.at(row_counter) = ms;
301 ++row_counter;
302 }
303
304 // final output difficulty for this interval
305 auto get_itv_diff(Calc& calc) const -> float
306 {
307 if (row_counter == 0) {
308 return 0.F;
309 }
310
311 // ms vals to counts
312 std::unordered_map<int, int> mode;
313 std::vector<float> static_ms;
314 for (int i = 0; i < row_counter; ++i) {
315 const auto v = static_cast<int>(calc.cj_static.at(i));
316 static_ms.push_back(calc.cj_static.at(i));
317 mode[v]++; // this is safe
318 }
319 auto modev = 0;
320 auto modefreq = 0;
321 for (auto it = mode.begin(); it != mode.end(); it++) {
322 if (it->second > modefreq) {
323 modev = it->first;
324 modefreq = it->second;
325 }
326 }
327 for (auto i = 0; i < static_ms.size(); i++) {
328 // weight = 0 means all values become modev
329 static_ms.at(i) = weighted_average(static_ms.at(i),
330 static_cast<float>(modev),
331 static_ms_weight,
332 1.F);
333 }
334
335 const auto ms_total = sum(static_ms);
336 const auto ms_mean = ms_total / static_cast<float>(row_counter);
337 return ms_to_scaled_nps(ms_mean);
338 }
339
340 void interval_end()
341 {
342 row_counter = 0;
343 }
344 void full_reset()
345 {
346 is_cj = false;
347 was_cj = false;
348 is_scj = false;
349 is_at_least_3_note_anch = false;
350 last_was_3_note_anch = false;
351
352 is_actually_continuing_jack = false;
353
354 last_row_count = 0;
355 last_last_row_count = 0;
356
357 last_row_notes = 0U;
358 last_last_row_notes = 0U;
359
360 chain = 1;
361
362 carpathian_basin_capsized_boat_chord_bonk.clear();
363 }
364
365 private:
366 int row_counter = 0;
367
368 bool is_cj = false;
369 bool was_cj = false;
370 bool is_scj = false;
371 bool is_at_least_3_note_anch = false;
372 bool last_was_3_note_anch = false;
373
374 bool is_actually_continuing_jack = false;
375
376 int last_row_count = 0;
377 int last_last_row_count = 0;
378
379 unsigned last_row_notes = 0U;
380 unsigned last_last_row_notes = 0U;
381
382 int chain = 1;
383
384 std::vector<int> carpathian_basin_capsized_boat_chord_bonk =
385 std::vector<int>();
386};
387
389struct techyo
390{
391
392 const std::string name = "TC_Static";
393
394#pragma region params
395
396 float tc_base_weight = 4.F;
397 float nps_base_weight = 9.F;
398 float rm_base_weight = 1.F;
399
400 float balance_comp_window = 36.F;
401 float chaos_comp_window = 4.F;
402 float tc_static_base_window = 2.F;
403
404 // determines steepness of non-1/2 balance ratios
405 float balance_power = 2.F;
406 float min_balance_ratio = 0.2F;
407 float balance_ratio_scaler = 1.F;
408
409 const std::vector<std::pair<std::string, float*>> _params{
410 { "tc_base_weight", &tc_base_weight },
411 { "nps_base_weight", &nps_base_weight },
412 { "rm_base_weight", &rm_base_weight },
413
414 { "balance_comp_window", &balance_comp_window },
415 { "chaos_comp_window", &chaos_comp_window },
416 { "tc_static_base_window", &tc_static_base_window },
417 { "balance_power", &balance_power },
418 { "min_balance_ratio", &min_balance_ratio },
419 { "balance_ratio_scaler", &balance_ratio_scaler },
420 };
421
422#pragma endregion params and param map
423
424 // moving window of the last 3 {column, time} that showed up
425 // we transform col_type ohj into 2 entries with the same time on insertion
426 static const unsigned trill_window = 3;
427 std::array<std::pair<col_type, float>, techyo::trill_window> mw_dt{};
428
429 void advance_base(const SequencerGeneral& seq,
430 const col_type& ct,
431 Calc& calc,
432 const int& hand,
433 const float& row_time)
434 {
435 if (row_counter >= max_rows_for_single_interval) {
436 return;
437 }
438
439 // process_mw_dt(ct, seq.get_any_ms_now());
440 // advance_trill_base(calc);
441 // increment_column_counters(ct);
442 // auto balance_comp = std::max(calc_balance_comp() *
443 // balance_ratio_scaler, min_balance_ratio);
444 auto chaos_comp = calc_chaos_comp(seq, ct, calc, hand, row_time);
445 // insert(balance_ratios, balance_comp);
446 teehee(chaos_comp);
447 calc.tc_static.at(row_counter) =
448 teehee.get_mean_of_window((int)tc_static_base_window);
449 ++row_counter;
450 }
451
452 void advance_rm_comp(const float& rm_diff)
453 {
454 rm_itv_max_diff = std::max(rm_itv_max_diff, rm_diff);
455 }
456
457 void advance_jack_comp(const float& hardest_itv_jack_ms) {
458 static const auto jack_base_scale = 1.01F;
459 jack_itv_diff = ms_to_scaled_nps(hardest_itv_jack_ms) * jack_base_scale;
460 }
461
462 // for debug
463 [[nodiscard]] auto get_itv_rma_diff() const -> float
464 {
465 return rm_itv_max_diff;
466 }
467
468 // not for debug
469 [[nodiscard]] auto get_itv_jack_diff() const -> float
470 {
471 return jack_itv_diff;
472 }
473
474 // final output difficulty for this interval
475 // the output of this is officially TechBase for an interval
476 [[nodiscard]] auto get_itv_diff(const float& nps_base, Calc& calc) const
477 -> float
478 {
479 auto rmbase = rm_itv_max_diff;
480 const auto nps_biased_chaos_base = weighted_average(
481 get_tc_base(calc), nps_base, tc_base_weight, nps_base_weight);
482 if (rmbase >= nps_biased_chaos_base) {
483 // for rm dominant intervals, use tc to drag diff down
484 // weight should be [0,1]
485 // 1 -> all rm
486 // 0 -> all tc
487 rmbase = weighted_average(
488 rmbase, nps_biased_chaos_base, rm_base_weight, 1.F);
489 }
490 return std::max(nps_biased_chaos_base, rmbase);
491
492 /*
493 const auto trillbase = get_tb_base(calc);
494 const auto jackbase = jack_itv_diff;
495
496 // [0,1]
497 // 0 = all jack/flam
498 // 1 = all trill
499 const auto how_flammy = get_itv_flam_factor();
500
501 const auto combinedbase =
502 weighted_average(trillbase, jackbase, how_flammy, 1.F);
503 return std::max(rmbase, combinedbase);
504 */
505 }
506
507 void interval_end()
508 {
509 row_counter = 0;
510 rm_itv_max_diff = 0.F;
511 jack_itv_diff = 0.F;
512 balance_ratios.fill(0);
513 //count_left.fill(0);
514 //count_right.fill(0);
515 }
516
517 void full_reset()
518 {
519 row_counter = 0;
520 rm_itv_max_diff = 0.F;
521 jack_itv_diff = 0.F;
522 teehee.zero();
523 count_left.fill(0);
524 count_right.fill(0);
525
526 mw_dt.fill({ col_empty, ms_init });
527 tb_static.fill(ms_init);
528 flammity.fill(0.F);
529 }
530
531 private:
532 // how many non-empty rows have we seen
533 int row_counter = 0;
534
535 // jank stuff.. keep a small moving average of the base diff
537
538 // 1 or 0 continuously
539 std::array<int, max_rows_for_single_interval> count_left{};
540 std::array<int, max_rows_for_single_interval> count_right{};
541
542 // balance ratio values for an interval
543 std::array<float, max_rows_for_single_interval> balance_ratios{};
544
545 // trill base values
546 std::array<float, max_rows_for_single_interval> tb_static{};
547 std::array<float, max_rows_for_single_interval> flammity{};
548
549 // max value of rm diff for this interval, this will be an exception to the
550 // only storing ms rule, rm diff will be stored as a pre-converted diff
551 // value because rm_sequencing may adjust the scaled diff using the rm
552 // components in ways we can't emulate here (nor should we try)
553 float rm_itv_max_diff = 0.F;
554 float jack_itv_diff = 0.F;
555
556 // get the interval base diff, which will then be merged via weighted
557 // average with npsbase, and then compared to max_rm diff
558 [[nodiscard]] auto get_tc_base(Calc& calc) const -> float
559 {
560 if (row_counter == 0) {
561 return 0.F;
562 }
563
564 auto ms_total = 0.F;
565 for (auto i = 0; i < row_counter; ++i) {
566 ms_total += calc.tc_static.at(i);
567 }
568
569 const auto ms_mean = ms_total / static_cast<float>(row_counter);
570 return ms_to_scaled_nps(ms_mean);
571 }
572
573 // produces the average value of the trill ms value for the interval
574 // converted to nps
575 auto get_tb_base(Calc& calc) const -> float
576 {
577 if (row_counter < 3) {
578 return 0.F;
579 }
580
581 auto ms_total = 0.F;
582 for (auto i = 0; i < row_counter; ++i) {
583 ms_total += tb_static.at(i);
584 }
585 const auto ms_mean = ms_total / static_cast<float>(row_counter);
586 return ms_to_scaled_nps(ms_mean);
587 }
588
589 // produces the average value of the flam value for the interval
590 // [0,1]
591 // 0 = all jacks or all flams
592 // 1 = all perfect trills
593 auto get_itv_flam_factor() const -> float
594 {
595 if (row_counter == 0) {
596 return 0.F;
597 }
598
599 auto total = 0.F;
600 for (auto i = 0; i < row_counter; ++i) {
601 total += flammity.at(i);
602 }
603
604 const auto mean = total / static_cast<float>(row_counter);
605 return mean;
606 }
607
612 float calc_balance_comp() const {
613
614 const auto left =
615 get_total_for_windowf(count_left, (unsigned)balance_comp_window);
616 const auto right =
617 get_total_for_windowf(count_right, (unsigned)balance_comp_window);
618
619 // for this application of balance, dont care about half empty hands
620 // or fully balanced hands
621 if (left == 0.F || right == 0.F) {
622 return 1.F;
623 }
624 if (left == right) {
625 return 1.F;
626 }
627
628 // make sure power is at least 2 and divisible by 2
629 // but it cant be greater than 30 due to int overflow
630 const auto bal_power =
631 std::clamp(static_cast<int>(std::max(balance_power, 2.F)) % 2 == 0
632 ? balance_power
633 : balance_power - 1.F,
634 2.F,
635 30.F);
636 const auto bal_factor = std::pow(2.F, bal_power);
637
638 const auto high = left > right ? left : right;
639 const auto low = left > right ? right : left;
640
641 const auto x = low / high;
642 const auto y = (-bal_factor * std::pow(x - 0.5F, bal_power)) + 1;
643 return y;
644 }
645
649 float calc_chaos_comp(const SequencerGeneral& seq,
650 const col_type& ct,
651 Calc& calc, const int& hand, const float& row_time)
652 {
653 const auto a = seq.get_sc_ms_now(ct);
654 float b;
655 if (ct == col_ohjump) {
656 b = seq.get_sc_ms_now(ct, false);
657 } else {
658 b = seq.get_cc_ms_now();
659 }
660
661 // geometric mean of ms times since (last note in this column) and (last note in the other column)
662 //const auto c = fastsqrt(a) * fastsqrt(b);
663
664 // arithmetic mean instead
665 const auto c = (a + b) / 2;
666
667 // coeff var. of last N ms times on either column
668 auto pineapple = seq._mw_any_ms.get_cv_of_window((unsigned)chaos_comp_window);
669 // coeff var. of last N ms times on left column
670 auto porcupine =
671 seq._mw_sc_ms[col_left].get_cv_of_window((unsigned)chaos_comp_window);
672 // coeff var. of last N ms times on right column
673 auto sequins =
674 seq._mw_sc_ms[col_right].get_cv_of_window((unsigned)chaos_comp_window);
675
676 // coeff var. is sd divided by mean
677 // cv of 0 is 0 sd
678
679 // all of those numbers are clamped to [0.5, 1.5] (or [oioi, ioio+oioi])
680 const auto oioi = 0.5F;
681 const auto ioio = 0.5F;
682 pineapple = std::clamp(pineapple + oioi, oioi, ioio + oioi);
683 porcupine = std::clamp(porcupine + oioi, oioi, ioio + oioi);
684 sequins = std::clamp(sequins + oioi, oioi, ioio + oioi);
685
686 // get most recent ms time in left column
687 const auto scoliosis = seq._mw_sc_ms[col_left].get_now();
688 // get most recent ms time in right column
689 const auto poliosis = seq._mw_sc_ms[col_right].get_now();
690
691 float obliosis;
692 if (ct == col_left) {
693 obliosis = poliosis / scoliosis;
694 } else {
695 obliosis = scoliosis / poliosis;
696 }
697
698 // the ratio of most recent ms times between left and right column must be [1,10]
699 // 1 = perfect trill or slowing down trill
700 // 10 = quickly speeding up trill (flams)
701 obliosis = std::clamp(obliosis, 1.F, 10.F);
702
703 // sqrt of ( [1,inf] - 1 ) (NOTE: fastsqrt IS NOT ACCURATE)
704 // result = [0,sqrt(inf)]
705 // 0 = perfect trill or perfect jumpjack
706 // >0 = uneven trill
707 // huge = one column is hitting notes faster than the other (maybe minijack in pattern)
708 auto pewp = fastsqrt(div_high_by_low(scoliosis, poliosis) - 1.F);
709
710 // [0,inf] divided by [1,10]
711 pewp /= obliosis;
712
713 if (calc.debugmode) {
714 std::array<float, 4> a;
715 a[0] = row_time;
716 a[1] = pewp;
717 a[2] = obliosis;
718 a[3] = c;
719 calc.debugTechVals.at(hand).emplace_back(a);
720 }
721
722 // average of (cv left, cv right, cv both)
723 // note cv clamped to [0.5,1.5]
724 // simplifies to [0.5,1.5]
725 // output: [0.5, 1.5]
726 const auto vertebrae = std::clamp(
727 ((pineapple + porcupine + sequins) / 3.F), oioi, ioio + oioi);
728
729 // result is ms divided by fudgy cv number
730 // [0,5000] / [0.5,1.5]
731 return c / vertebrae;
732 }
733
734 void advance_trill_base(Calc& calc)
735 {
736 auto flamentation = .5F;
737 auto trill_ms_value = ms_init;
738
739 auto& a = mw_dt.at(0);
740 if (a.first == col_init) {
741 // do nothing special, not a complete trill
742 } else {
743 auto& b = mw_dt.at(1);
744 auto& c = mw_dt.at(2);
745
746 auto flam_of_the_trill = ms_init;
747 auto third_tap = ms_init;
748
749 if (b.second == 0.F && a.first != b.first) {
750 // first 2 notes form a jump
751 flam_of_the_trill = 0.F;
752 third_tap = c.second * 2.F;
753 } else if (c.second == 0.F && b.first != c.first) {
754 // last 2 notes form a jump
755 flam_of_the_trill = 0.F;
756 third_tap = b.second * 2.F;
757 } else {
758 // determine ... trillflamjackyness
759
760 if (a.first == c.first && a.first != b.first) {
761 // it's a trill (121 or 212)
762 flam_of_the_trill = std::min(b.second, c.second);
763 third_tap = std::max(b.second, c.second);
764 } else if (a.first == c.first) {
765 // it's 111
766 // the intended behavior is to treat it like a flam
767 // thus treating it like a jack
768 flam_of_the_trill = 0.F;
769 third_tap = c.second;
770 } else {
771 if (a.first != b.first) {
772 // it's 211
773 flam_of_the_trill = b.second;
774 third_tap = c.second;
775 } else {
776 // it's 112
777 flam_of_the_trill = c.second;
778 third_tap = b.second;
779 }
780 }
781 }
782
783 if (flam_of_the_trill == 0.F && third_tap == 0.F) {
784 // effectively a forced dropped note
785 flamentation = 0.F;
786 trill_ms_value = 0.1F;
787 } else {
788 // ratio = [0,1]
789 // 0 = flam involved
790 // 1 = straight trill
791 const auto flam_ms_depressor = 0.F;
792 auto ratio = div_low_by_high(
793 std::max(flam_of_the_trill - flam_ms_depressor, 0.F),
794 third_tap);
795 flamentation = std::clamp(std::pow(ratio, 0.125F), 0.F, 1.F);
796 trill_ms_value = (flam_of_the_trill + third_tap) / 2.F;
797 }
798 }
799
800 flammity.at(row_counter) = flamentation;
801 tb_static.at(row_counter) = trill_ms_value;
802 }
803
804
806 // util
807 void increment_column_counters(const col_type& ct)
808 {
809 switch (ct) {
810 case col_left: {
811 insert(count_left, 1);
812 insert(count_right, 0);
813 break;
814 }
815 case col_right: {
816 insert(count_left, 0);
817 insert(count_right, 1);
818 break;
819 }
820 case col_ohjump: {
821 insert(count_left, 1);
822 insert(count_right, 1);
823 break;
824 }
825 default: {
826 insert(count_left, 0);
827 insert(count_right, 0);
828 break;
829 }
830 }
831 }
832
833 float get_total_for_windowf(
834 const std::array<int, max_rows_for_single_interval>& arr, const unsigned window) const
835 {
836 float o = 0.F;
837 auto i = max_rows_for_single_interval;
838 while (i > max_rows_for_single_interval - (int)window) {
839 i--;
840 o += arr.at(i);
841 }
842 return o;
843 }
844
845 template <typename T>
846 void insert(std::array<T, max_rows_for_single_interval>& arr,
847 const T value)
848 {
849 for (auto i = 1; i < max_rows_for_single_interval; i++) {
850 arr.at(i - 1) = arr.at(i);
851 }
852 arr.at(max_rows_for_single_interval - 1) = value;
853 }
854
855 void process_mw_dt(const col_type& ct, const float ms_now)
856 {
857 auto& arr = mw_dt;
858 if (ct == col_ohjump) {
859 for (auto i = 1; i < trill_window; i++) {
860 arr.at(i - 1) = arr.at(i);
861 }
862 arr.at(trill_window - 1) = { col_left, ms_now };
863
864 for (auto i = 1; i < trill_window; i++) {
865 arr.at(i - 1) = arr.at(i);
866 }
867 arr.at(trill_window - 1) = { col_right, 0.F };
868 } else {
869 for (auto i = 1; i < trill_window; i++) {
870 arr.at(i - 1) = arr.at(i);
871 }
872 arr.at(trill_window - 1) = { ct, ms_now };
873 }
874 }
875};
876
877
879{
880 int len = 1;
881 float last_ms = ms_init;
882 float max_ms = ms_init;
883 float len_capped_ms = ms_init;
884 float last_note_sec = s_init;
885 float start_note_sec = s_init;
886 inline void reset()
887 {
888 len = 1;
889 last_ms = ms_init;
890 max_ms = ms_init;
891 len_capped_ms = ms_init;
892 last_note_sec = s_init;
893 start_note_sec = s_init;
894 }
895 inline void check_reset(const float& now) {
896 auto since_last = ms_from(now, last_note_sec);
897 if (since_last > guaranteed_reset_buffer_ms) {
898 reset();
899 }
900 }
901 inline void operator()(const float& now)
902 {
903 last_ms = ms_from(now, last_note_sec);
904 if (last_ms > max_ms + jack_spacing_buffer_ms ||
905 last_ms * jack_speed_increase_cutoff_factor < max_ms) {
906 // too slow reset, too fast reset
907 start_note_sec = last_note_sec;
908 len = 2;
909 } else {
910 len++;
911 }
912 max_ms = last_ms;
913 last_note_sec = now;
914 }
915 inline float get_ms() {
916 if (len > jack_len_cap) {
917 return len_capped_ms;
918 }
919 static const auto avg_ms_mult = 1.5F;
920 static const auto anchor_time_buffer_ms = 30.F;
921 static const auto min_ms = 95.F;
922 const auto total_ms = ms_from(last_note_sec, start_note_sec);
923 const auto _len = static_cast<float>(len - 1);
924 const auto avg_ms = total_ms / _len;
925 const auto adj_total_ms =
926 total_ms + anchor_time_buffer_ms + avg_ms * avg_ms_mult;
927 auto ms = adj_total_ms / _len;
928 if (len == 2) {
929 ms *= 1.1F;
930 ms = ms < 180.F ? 180.F : ms;
931 }
932 ms = ms < min_ms ? min_ms : ms;
933 if (std::isnan(ms))
934 ms = max_ms;
935 if (len == jack_len_cap) {
936 len_capped_ms = ms;
937 }
938 return ms;
939 }
940};
942{
943 std::vector<jack_col> sequencers{};
944
945 std::vector<unsigned> left_hand_mask{};
946 std::vector<unsigned> right_hand_mask{};
947
948 void init(const int& keycount) {
949 sequencers = std::vector<jack_col>(keycount);
950 left_hand_mask.clear();
951 right_hand_mask.clear();
952 for (auto i = 0; i < keycount / 2; i++) {
953 left_hand_mask.push_back(i);
954 }
955 /*
956 if (keycount > 2 && keycount % 2 != 0) {
957 left_hand_mask.push_back(keycount / 2);
958 }
959 */
960 for (auto i = keycount / 2; i < keycount; i++) {
961 right_hand_mask.push_back(i);
962 }
963 reset();
964 }
965
966 void reset() {
967 for (auto& seq : sequencers) {
968 seq.reset();
969 }
970 }
971
972 void operator()(const int& column,
973 const float& now)
974 {
975 for (auto& seq : sequencers) {
976 seq.check_reset(now);
977 }
978 sequencers.at(column)(now);
979 }
980
981 float get_lowest_jack_ms(unsigned hand, Calc& calc) {
982 auto& mask = hand == left_hand ? left_hand_mask : right_hand_mask;
983
984 auto min = ms_init;
985 for (auto& col : mask) {
986 const auto v = sequencers.at(col).get_ms();
987 if (v < min) {
988 min = v;
989 }
990 }
991 return min;
992 }
993
994};
995
996struct diffz
997{
998 nps _nps;
999 techyo _tc;
1000 ceejay _cj;
1001
1002 void interval_end()
1003 {
1004 _tc.interval_end();
1005 _cj.interval_end();
1006 }
1007
1008 void full_reset()
1009 {
1010 interval_end();
1011 _cj.full_reset();
1012 _tc.full_reset();
1013 }
1014};
Main driver class for the difficulty calculator as a whole.
Definition MinaCalc.h:82
std::array< std::vector< std::array< float, 4 > >, num_hands > debugTechVals
per hand vector of arrays: techyo chaos values of [row_time, pewp, obliosis, c]
Definition MinaCalc.h:289
std::array< std::array< std::vector< float >, NUM_CalcDiffValue >, num_hands > init_base_diff_vals
Definition MinaCalc.h:187
int numitv
Total number of intervals for the current file/rate (one per half second)
Definition MinaCalc.h:263
std::array< float, max_rows_for_single_interval > tc_static
Definition MinaCalc.h:255
float grindscaler
Definition MinaCalc.h:270
std::vector< std::array< RowInfo, max_rows_for_single_interval > > adj_ni
Definition MinaCalc.h:162
bool debugmode
For debug output. Should only ever be true at music select.
Definition MinaCalc.h:95
std::array< std::vector< int >, num_hands > itv_points
Definition MinaCalc.h:171
std::array< float, max_rows_for_single_interval > cj_static
Definition MinaCalc.h:260
std::vector< int > itv_size
Number of rows in each interval.
Definition MinaCalc.h:165
Definition CalcWindow.h:15
void zero()
set everything to zero
Definition CalcWindow.h:210
auto get_mean_of_window(const int &window) const -> float
get the mean for the moving window up to a given size
Definition CalcWindow.h:85
auto get_cv_of_window(const int &window) const -> float
get the coefficient of variance of the moving window up to a given size
Definition CalcWindow.h:113
Definition GenericSequencing.h:422
std::array< CalcMovingWindow< float >, num_cols_per_hand > _mw_sc_ms
Definition GenericSequencing.h:435
CalcMovingWindow< float > _mw_any_ms
Definition GenericSequencing.h:428
Definition SequencedBaseDiffCalc.h:204
Definition SequencedBaseDiffCalc.h:997
Definition SequencedBaseDiffCalc.h:879
Definition SequencedBaseDiffCalc.h:66
static void grindscale(Calc &calc)
determine grindscaler using smoothed npsbase
Definition SequencedBaseDiffCalc.h:153
static void actual_cancer(Calc &calc, const int &hand)
determine NPSBase, itv_points, CJBase for this hand
Definition SequencedBaseDiffCalc.h:68
Definition SequencedBaseDiffCalc.h:942
if this looks ridiculous, that's because it is
Definition SequencedBaseDiffCalc.h:390