-
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Meter.h
249 lines (200 loc) · 8.74 KB
/
Meter.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
/** */
struct DecibelHelpers
{
enum
{
// The maximum gain value of the slider in decibels.
maxSliderLevelDb = 12,
// The minimum decibel level of the slider.
minSliderLevelDb = -100,
// The magnitude of spatial compression of lower decibel values versus higher values.
// Ranges from 0 (linear) to infinity (hard knee).
curveTensionDb = 6
};
/** @returns the point at which the volume slider should snap.
@param snapDb The value in decibels which this slider should snap to
*/
static double calculateSnapPoint (double snapDb) noexcept;
/** @returns the proportion of the meter length for the gain value.
@param gain The absolute volume level.
@param minimumDecibels The minimum level of the meter in decibels.
@param maximumDecibels The maximum level of the meter in decibels.
*/
static double gainToMeterProportion (double gain,
int minimumDecibels = minSliderLevelDb,
int maximumDecibels = maxSliderLevelDb) noexcept;
/** @returns the gain value for the porportion of the meter length.
@param meterProportion The proportion of the meter length.
@param minimumDecibels The minimum level of the meter in decibels.
@param maximumDecibels The maximum level of the meter in decibels.
*/
static double meterProportionToGain (double meterProportion,
int minimumDecibels = minSliderLevelDb,
int maximumDecibels = maxSliderLevelDb) noexcept;
/** @returns the proportion of the meter length for the decibel value.
*
@param meterProportion The proportion of the meter length.
@param minimumDecibels The minimum level of the meter in decibels.
@param maximumDecibels The maximum level of the meter in decibels.
*/
static double meterProportionToDecibels (double meterProportion,
int minimumDecibels = minSliderLevelDb,
int maximumDecibels = maxSliderLevelDb) noexcept;
/** @returns the proportion of the meter height for the decibel value.
*
@param decibels The volume value in decibels.
@param minimumDecibels The minimum level of the meter in decibels
@param maximumDecibels The maximum level of the meter in decibels
*/
static double decibelsToMeterProportion (double decibels,
int minimumDecibels = minSliderLevelDb,
int maximumDecibels = maxSliderLevelDb) noexcept;
/** Maps values on [0,1] onto a tension curve of the same interval.
Used to spatially differentiate higher decibel values.
The curve compresses lower values and expands higher values:
1 | /'
| / `
| / ,
| / .
| /,
|/`_____
0 1
@param value: the value to transform.
*/
static double linearToCurved (double value) noexcept;
/** @returns the inverse of linearToCurved().
This curve expandes lower values and compresses higher values:
1 | ,/
| ' /
| ` /
|. /
|,/
|/_____
0 1
@param curvedValue: the value to transform.
*/
static double curvedToLinear (double curvedValue) noexcept;
private:
SQUAREPINE_DECLARE_TOOL_CLASS (DecibelHelpers)
};
//==============================================================================
/** */
class Meter final : public Component
{
public:
//==============================================================================
/** */
class Model
{
public:
/** */
virtual ~Model() noexcept = default;
/** @returns the expiration time of the maximum meter level, after which it decays. */
virtual int64 getExpiryTimeMs() const noexcept { return 3000; }
/** @returns */
virtual bool needsMaxLevel() const noexcept { return false; }
/** @returns */
virtual bool isHorizontal() const noexcept { return false; }
/** @returns */
virtual Array<float> getChannelLevels() const = 0;
/** */
struct ColourPosition final
{
ColourPosition() noexcept = default;
ColourPosition (Colour c, double d) noexcept :
colour (c),
decibels (d)
{
}
Colour colour;
double decibels = 0.0;
};
/** @returns */
virtual std::array<ColourPosition, 3> getColourPositions() const
{
return
{
ColourPosition (Colours::red, 0.0),
ColourPosition (Colours::yellow, -9.0),
ColourPosition (Colours::green, -18.0)
};
}
};
//==============================================================================
/** */
Meter (Model* model_ = nullptr);
//==============================================================================
/** */
void setModel (Model*);
/** */
Model* getModel() const noexcept { return model; }
//==============================================================================
/** @returns true if the levels have changed. */
bool refresh();
//==============================================================================
/** */
struct ChannelContext
{
ChannelContext() = default;
ChannelContext (const ChannelContext&) = default;
ChannelContext (ChannelContext&&) = default;
~ChannelContext() = default;
ChannelContext& operator= (const ChannelContext&) = default;
ChannelContext& operator= (ChannelContext&&) = default;
void setLevel (float newLevel) { set (level, newLevel); }
float getLevel() const noexcept { return level; }
void setLastLevel (float newLastLevel) { set (lastLevel, newLastLevel); }
float getLastLevel() const noexcept { return lastLevel; }
void setMaxLevel (float newMaxLevel) { set (maxLevel, newMaxLevel); }
float getMaxLevel() const noexcept { return maxLevel; }
void setLastMaxLevel (float newLastMaxLevel) { set (lastMaxLevel, newLastMaxLevel); }
float getLastMaxLevel() const noexcept { return lastMaxLevel; }
void setLastMaxAudioLevelTime (int64 t) { timeOfMaximumMs = t; }
int64 getLastMaxAudioLevelTime() const noexcept { return timeOfMaximumMs; }
void setMeterArea (const Rectangle<int>& area) { meterArea = area; }
const Rectangle<int>& getMeterArea() const noexcept { return meterArea; }
private:
float level = 0.0f; // The last measured audio absolute volume level.
float lastLevel = 0.0f; // The volume level of the last update, used to check if levels have changed for repainting.
float maxLevel = 0.0f; // The maximum audio levels of the trailing 3 seconds.
float lastMaxLevel = 0.0f; // The max volume level of the last update.
int64 timeOfMaximumMs = 0; // The time of the last maximum audio level.
Rectangle<int> meterArea; // The left/right drawable regions for the meter.
void set (float& value, float newValue)
{
dsp::util::snapToZero (newValue);
value = newValue;
}
};
/** */
const ChannelContext& getChannel (int channel) const noexcept { return channels.getReference (channel); }
/** */
float getChannelLevel (int channel) const noexcept { return getChannel (channel).getLevel(); }
//==============================================================================
/** */
enum class ClippingLevel
{
none = 0,
warning,
clipping
};
/** */
ClippingLevel getClippingLevel() const noexcept { return clippingLevel; }
/** */
void resetClippingLevel() { clippingLevel = ClippingLevel::none; }
//==============================================================================
/** @internal */
void resized() override;
/** @internal */
void paint (Graphics&) override;
private:
//==============================================================================
Model* model = nullptr;
Array<ChannelContext> channels;
Array<float> levels;
ClippingLevel clippingLevel = ClippingLevel::none;
//==============================================================================
void updateClippingLevel (bool timeToUpdate);
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Meter)
};