Etterna 0.74.4
Loading...
Searching...
No Matches
Minijack.h
1#pragma once
2#include "../IntervalHandInfo.h"
3
8{
9 const CalcPatternMod _pmod = Minijack;
10 const std::string name = "MinijackMod";
11
12#pragma region params
13
14 float min_mod = 1.F;
15 float max_mod = 1.25F;
16 float base = 0.4F;
17
18 float mj_scaler = 2.6F;
19 float mj_buffer = 0.3F;
20
21 const std::vector<std::pair<std::string, float*>> _params{
22 { "min_mod", &min_mod },
23 { "max_mod", &max_mod },
24 { "base", &base },
25
26 { "minijack_scaler", &mj_scaler },
27 { "minijack_buffer", &mj_buffer },
28 };
29#pragma endregion params and param map
30
31 // ms times for each finger
33 CalcMovingWindow<float> right_ms{};
34
35 // 2.0 is equivalent to 8th -> 16th
36 // aka a 16th minijack in 16th js
37 // if the min of the window is
38 const float minijack_speed_increase_factor = 1.9F;
39
40 // if the ms gap after a minijack larger by this factor
41 // then the ms gap for the minijack is confirmed a minijack
42 const float minijack_confirmation_factor = 1.3F;
43
44 // throw out data if this many ms pass
45 const float dont_care_threshold = 500.F;
46
47 // 150ms is a 100 bpm 16th
48 const float slow_minijack_cutoff_ms = 149.5F;
49
50 const int window = 3;
51 int minijacks = 0;
52 float pmod = min_mod;
53
54 int left_since_last_right = 0;
55 int right_since_last_left = 0;
56 CalcMovingWindow<int> left_notes{};
57 CalcMovingWindow<int> right_notes{};
58
59 // tracking taps that occur on the opposite hand
60 int off_since_last_on = 0;
61 CalcMovingWindow<int> off_hand_notes{};
62
63#pragma region generic functions
64
65 void full_reset()
66 {
67 pmod = neutral;
68 minijacks = 0;
69 left_ms.fill(ms_init);
70 right_ms.fill(ms_init);
71 left_notes.fill(0);
72 right_notes.fill(0);
73 off_hand_notes.fill(0);
74 left_since_last_right = 0;
75 right_since_last_left = 0;
76 off_since_last_on = 0;
77 }
78
79 void reset_mw_for_ct(const col_type& ct) {
80 switch (ct) {
81 case col_left: {
82 left_ms.fill(ms_init);
83 break;
84 }
85 case col_right: {
86 right_ms.fill(ms_init);
87 break;
88 }
89 case col_ohjump: {
90 left_ms.fill(ms_init);
91 right_ms.fill(ms_init);
92 break;
93 }
94 default:
95 break;
96 }
97 }
98
99#pragma endregion
100
101 void minijack_check(const CalcMovingWindow<float>& mv, const CalcMovingWindow<int>& mwOffTapCounts) {
102 // make sure the window is filled out
103 const auto max = mv.get_max_for_window(window);
104 if (max != ms_init) {
105 auto i = max_moving_window_size;
106 const auto min = mv.get_min_for_window(window);
107 const auto recent_ms = mv[--i];
108 const auto last_ms = mv[--i];
109 const auto last_last_ms = mv[--i];
110
111 // basically we dont care if the "minijack" exists
112 // if it is so slow
113 if (last_ms > slow_minijack_cutoff_ms) {
114 return;
115 }
116
117 // for a minijack to count
118 // it must have a gap before it and after
119 // the gap before : speedup of basically 8th -> 16th or faster
120 // the gap after : slowdown of basically 16th -> 12th or slower
121 if (last_ms == min &&
122 recent_ms > last_ms * minijack_confirmation_factor &&
123 last_last_ms > last_ms * minijack_speed_increase_factor) {
124 // require that the taps which fit the minijack speed condition
125 // have no off tap between. this would make it a trill instead.
126 if (mwOffTapCounts[max_moving_window_size - 2] == 0) {
127 // nerf case:
128 // require that the minijack is not part of a two hand trill
129 if (off_hand_notes[max_moving_window_size - 2] == 0) {
130 minijacks++;
131 }
132 }
133 }
134 }
135 }
136
137 void advance_sequencing(const col_type& ct,
138 const float& ms_now)
139 {
140 /*
141 if (ms_now > dont_care_threshold) {
142 // data has become stale
143 reset_mw_for_ct(ct);
144 return;
145 }
146 */
147
148 switch (ct) {
149 case col_left: {
150 // if we see a left note after having seen right notes
151 // we just went through a trill
152 if (right_since_last_left > 0 || left_since_last_right > 0) {
153 right_notes(right_since_last_left);
154 }
155 left_since_last_right++;
156 right_since_last_left = 0;
157 left_ms(ms_now);
158 commit_off_hand_taps();
159 minijack_check(left_ms, right_notes);
160 break;
161 }
162 case col_right: {
163 // if we see a right note after x lefts...
164 // trill happen
165 if (left_since_last_right > 0 || right_since_last_left > 0) {
166 left_notes(left_since_last_right);
167 }
168 right_since_last_left++;
169 left_since_last_right = 0;
170 right_ms(ms_now);
171 commit_off_hand_taps();
172 minijack_check(right_ms, left_notes);
173 break;
174 }
175 case col_ohjump: {
176 // jumps reset trill conditions
177 // 1[12] and [12]1 considered minijacks
178 left_notes(left_since_last_right);
179 right_notes(right_since_last_left);
180 left_since_last_right = 0;
181 right_since_last_left = 0;
182 left_ms(ms_now);
183 right_ms(ms_now);
184 commit_off_hand_taps();
185 minijack_check(left_ms, right_notes);
186 minijack_check(right_ms, left_notes);
187 break;
188 }
189 default:
190 break;
191 }
192 }
193
194 void advance_off_hand_sequencing() {
195 off_since_last_on++;
196 }
197
198 void commit_off_hand_taps() {
199 off_hand_notes(off_since_last_on);
200 off_since_last_on = 0;
201 }
202
203 void set_pmod(const ItvHandInfo& itvhi)
204 {
205 if (minijacks == 0 || itvhi.get_taps_nowi() == 0) {
206 pmod = neutral;
207 return;
208 }
209
210 const auto mj = (static_cast<float>(minijacks) + mj_buffer) * mj_scaler;
211 const auto taps = itvhi.get_taps_nowf() - mj_buffer;
212
213 pmod = base + mj / taps;
214
215 pmod = std::clamp(pmod, min_mod, max_mod);
216 }
217
218 auto operator()(const ItvHandInfo& itvhi) -> float
219 {
220 set_pmod(itvhi);
221
222 interval_end();
223 return pmod;
224 }
225
226 void interval_end()
227 {
228 minijacks = 0;
229 }
230};
Definition CalcWindow.h:15
auto get_max_for_window(const int &window) const -> T
get the max for the moving window up to a given size
Definition CalcWindow.h:59
auto get_min_for_window(const int &window) const -> T
get the min for the moving window up to a given size
Definition CalcWindow.h:72
accumulates hand specific info across an interval as it's processed by row
Definition IntervalHandInfo.h:6
auto get_taps_nowi() const -> int
access functions for hand tap counts
Definition IntervalHandInfo.h:135
auto get_taps_nowf() const -> float
cast to float for divisioning and clean screen
Definition IntervalHandInfo.h:141
Definition Minijack.h:8