Etterna 0.75.1
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, 0b100 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// outputs 0b0000 for 4, 0b010 for 3, etc
58inline auto
59middle_mask(const unsigned& keycount) -> unsigned
60{
61 return ~mask_to_remove_middle_column(keycount);
62}
63
64// count number of 1's in noterow binary
65inline auto
66column_count(const unsigned& notes) -> int
67{
68 return std::popcount(notes);
69}
70
71inline auto
72is_only_1_bit(const unsigned& notes) -> bool
73{
74 return notes && !(notes & (notes - 1));
75}
76
77// return a vector of which columns are not empty
78// 0 is the leftmost column
79inline auto
80find_non_empty_cols(const unsigned& notes) -> std::vector<unsigned>
81{
82 std::vector<unsigned> o{};
83 for (auto i = 0u; 1u << i <= notes; i++) {
84 if (((1u << i) & notes) != 0u) {
85 o.push_back(i);
86 }
87 }
88 return o;
89}
90
92inline auto
93ms_from(const float& now, const float& last) -> float
94{
95 return (now - last) * 1000.F;
96}
97
99inline auto
100ms_to_bpm(const float& x) -> float
101{
102 return 15000.F / x;
103}
104
106inline auto
107ms_to_nps(const float& x) -> float
108{
109 return 1000.F / x;
110}
111
114inline auto
115ms_to_scaled_nps(const float& ms) -> float
116{
117 return ms_to_nps(ms) * finalscaler;
118}
119
120inline auto
121max_val(const std::vector<int>& v) -> int
122{
123 return *std::max_element(v.begin(), v.end());
124}
125
126inline auto
127max_val(const std::vector<float>& v) -> float
128{
129 return *std::max_element(v.begin(), v.end());
130}
131
132inline auto
133max_index(const std::vector<float>& v) -> int
134{
135 return static_cast<int>(
136 std::distance(v.begin(), std::max_element(v.begin(), v.end())));
137}
138
141
142// it's expected that successive row deltas will often be equal,
143// but this is not reliable in floating point. so any time you
144// want to branch on a comparison you need to assume that very
145// close values are supposed to be equal
146
147// floats are good to around 1e-5, milliseconds scale that up by 1e3,
148// if we stick to small-brain base 10 then 0.1f is all we have left
149
151constexpr float any_ms_epsilon = 0.1F;
152
154inline auto
155any_ms_is_greater(float a, float b) -> bool
156{
157 return (a - b) > any_ms_epsilon;
158}
159
161inline auto
162any_ms_is_lesser(float a, float b) -> bool
163{
164 return (b - a) > any_ms_epsilon;
165}
166
168inline auto
169any_ms_is_close(float a, float b) -> bool
170{
171 return fabsf(a - b) <= any_ms_epsilon;
172}
173
175inline auto
176any_ms_is_zero(float a) -> bool
177{
178 return any_ms_is_close(a, 0.F);
179}