Etterna 0.74.4
Loading...
Searching...
No Matches
SequencingHelpers.h
1#pragma once
2#include <array>
3#include <algorithm>
4#include <bit>
5#include <cmath>
6
7/* generic sequencing functions and defs to help either agnostic or dependent
8 * sequencers do their stuff */
9
11constexpr float s_init = -5.F;
13constexpr float ms_init = 5000.F;
14
16constexpr float finalscaler = 3.632F * 1.06F;
17
19static const bool ignore_middle_column = true;
20
21// outputs 0b1111 for 4, 0b111 for 3, etc
22inline auto
23keycount_to_bin(const unsigned& keycount) -> unsigned
24{
25 if (keycount < 2)
26 return 0b11;
27 return ~(~1u << (keycount - 1u));
28}
29
30// outputs 0b1100 for 4, 0b110 for 3, etc
31inline auto
32right_mask(const unsigned& keycount) -> unsigned
33{
34 if (keycount <= 2)
35 return 0b10;
36 return keycount_to_bin(keycount) >> (keycount / 2) << (keycount / 2);
37}
38
39// outputs 0b0011 for 4, 0b001 for 3, etc
40inline auto
41left_mask(const unsigned& keycount) -> unsigned
42{
43 const auto m = right_mask(keycount);
44 return ~m & static_cast<int>(std::exp2(std::ceil(std::log2(m))) - 1);
45}
46
47// outputs 0b1111 for 4, 0b101 for 3, etc
48inline auto
49mask_to_remove_middle_column(const unsigned& keycount) -> unsigned
50{
51 if (keycount % 2 == 0) {
52 return keycount_to_bin(keycount);
53 }
54 return keycount_to_bin(keycount) ^ (0b1 << (keycount / 2));
55}
56
57// count number of 1's in noterow binary
58inline auto
59column_count(const unsigned& notes) -> int
60{
61 return std::popcount(notes);
62}
63
64inline auto
65is_only_1_bit(const unsigned& notes) -> bool
66{
67 return notes && !(notes & (notes - 1));
68}
69
70// return a vector of which columns are not empty
71// 0 is the leftmost column
72inline auto
73find_non_empty_cols(const unsigned& notes) -> std::vector<unsigned>
74{
75 std::vector<unsigned> o{};
76 for (auto i = 0u; 1u << i <= notes; i++) {
77 if (((1u << i) & notes) != 0u) {
78 o.push_back(i);
79 }
80 }
81 return o;
82}
83
85inline auto
86ms_from(const float& now, const float& last) -> float
87{
88 return (now - last) * 1000.F;
89}
90
92inline auto
93ms_to_bpm(const float& x) -> float
94{
95 return 15000.F / x;
96}
97
99inline auto
100ms_to_nps(const float& x) -> float
101{
102 return 1000.F / x;
103}
104
107inline auto
108ms_to_scaled_nps(const float& ms) -> float
109{
110 return ms_to_nps(ms) * finalscaler;
111}
112
113inline auto
114max_val(const std::vector<int>& v) -> int
115{
116 return *std::max_element(v.begin(), v.end());
117}
118
119inline auto
120max_val(const std::vector<float>& v) -> float
121{
122 return *std::max_element(v.begin(), v.end());
123}
124
125inline auto
126max_index(const std::vector<float>& v) -> int
127{
128 return static_cast<int>(
129 std::distance(v.begin(), std::max_element(v.begin(), v.end())));
130}
131
134
135// it's expected that successive row deltas will often be equal,
136// but this is not reliable in floating point. so any time you
137// want to branch on a comparison you need to assume that very
138// close values are supposed to be equal
139
140// floats are good to around 1e-5, milliseconds scale that up by 1e3,
141// if we stick to small-brain base 10 then 0.1f is all we have left
142
144constexpr float any_ms_epsilon = 0.1F;
145
147inline auto
148any_ms_is_greater(float a, float b) -> bool
149{
150 return (a - b) > any_ms_epsilon;
151}
152
154inline auto
155any_ms_is_lesser(float a, float b) -> bool
156{
157 return (b - a) > any_ms_epsilon;
158}
159
161inline auto
162any_ms_is_close(float a, float b) -> bool
163{
164 return fabsf(a - b) <= any_ms_epsilon;
165}
166
168inline auto
169any_ms_is_zero(float a) -> bool
170{
171 return any_ms_is_close(a, 0.F);
172}