Loading...
Searching...
No Matches
NoteValues.hpp
1#pragma once
2#include <ossia/detail/math.hpp>
3
4#include <cmath>
5
6#include <cstdint>
7#include <iterator>
8#include <string_view>
9
13{
15{
16 float fraction; // of a whole note
17 const char* label;
18};
19
20// Sorted by increasing duration so turning the knob up always means longer.
21// The time chooser's twelve legacy ratios are all present so existing
22// documents round-trip exactly.
23inline constexpr NoteValue table[] = {
24 {1.f / 64.f, "1/64"}, {1.f / 48.f, "1/32T"}, {1.f / 32.f, "1/32"},
25 {1.f / 24.f, "1/16T"}, {3.f / 64.f, "1/32."}, {1.f / 16.f, "1/16"},
26 {1.f / 12.f, "1/8T"}, {3.f / 32.f, "1/16."}, {1.f / 8.f, "1/8"},
27 {1.f / 6.f, "1/4T"}, {3.f / 16.f, "1/8."}, {1.f / 4.f, "1/4"},
28 {1.f / 3.f, "1/2T"}, {3.f / 8.f, "1/4."}, {1.f / 2.f, "1/2"},
29 {2.f / 3.f, "1/1T"}, {3.f / 4.f, "1/2."}, {1.f, "1/1"},
30 {3.f / 2.f, "1/1."}, {2.f, "2/1"}, {3.f, "2/1."},
31 {4.f, "4/1"},
32};
33inline constexpr int count = std::ssize(table);
34inline constexpr int eighth = 8;
35
36enum class Feel : uint8_t
37{
38 Straight,
39 Dotted,
40 Triplet
41};
42
43constexpr Feel feelOf(int index) noexcept
44{
45 std::string_view l = table[index].label;
46 if(l.back() == '.')
47 return Feel::Dotted;
48 if(l.back() == 'T')
49 return Feel::Triplet;
50 return Feel::Straight;
51}
52
54constexpr double position(int index) noexcept
55{
56 return index / double(count - 1);
57}
58
60inline int indexAt(double v01) noexcept
61{
62 return ossia::clamp(int(std::lround(v01 * (count - 1))), 0, count - 1);
63}
64
68inline double snapPosition(double v01, Feel feel, bool fullTable) noexcept
69{
70 const double target = ossia::clamp(v01, 0., 1.) * (count - 1);
71 if(fullTable)
72 return std::lround(target) / double(count - 1);
73 int best = -1;
74 double bestDist = 0.;
75 for(int i = 0; i < count; i++)
76 {
77 if(feelOf(i) != feel)
78 continue;
79 const double d = std::abs(target - i);
80 if(best < 0 || d < bestDist)
81 {
82 bestDist = d;
83 best = i;
84 }
85 }
86 return best / double(count - 1);
87}
88
90inline int nearest(float frac) noexcept
91{
92 int best = 0;
93 float bestDist = std::abs(table[0].fraction - frac);
94 for(int i = 1; i < count; i++)
95 {
96 const float d = std::abs(table[i].fraction - frac);
97 if(d < bestDist)
98 {
99 bestDist = d;
100 best = i;
101 }
102 }
103 return best;
104}
105
107inline int nearest(float frac, Feel feel) noexcept
108{
109 int best = -1;
110 float bestDist = 0.f;
111 for(int i = 0; i < count; i++)
112 {
113 if(feelOf(i) != feel)
114 continue;
115 const float d = std::abs(table[i].fraction - frac);
116 if(best < 0 || d < bestDist)
117 {
118 bestDist = d;
119 best = i;
120 }
121 }
122 return best;
123}
124
126inline float straightLength(int index) noexcept
127{
128 switch(feelOf(index))
129 {
130 case Feel::Dotted:
131 return table[index].fraction / 1.5f;
132 case Feel::Triplet:
133 return table[index].fraction * 1.5f;
134 default:
135 return table[index].fraction;
136 }
137}
138
139inline float withFeel(float straight, Feel feel) noexcept
140{
141 switch(feel)
142 {
143 case Feel::Dotted:
144 return straight * 1.5f;
145 case Feel::Triplet:
146 return straight / 1.5f;
147 default:
148 return straight;
149 }
150}
151}
Definition NoteValues.hpp:13
double snapPosition(double v01, Feel feel, bool fullTable) noexcept
Definition NoteValues.hpp:68
int indexAt(double v01) noexcept
The note value at a knob position.
Definition NoteValues.hpp:60
float straightLength(int index) noexcept
How long the note is, straight: a dotted note is 3/2 of it, a triplet 2/3.
Definition NoteValues.hpp:126
int nearest(float frac) noexcept
The note value nearest to a fraction of a whole note.
Definition NoteValues.hpp:90
Definition NoteValues.hpp:15