|
1 | 1 | /** */
|
2 |
| -struct DecibelHelpers |
| 2 | +struct DecibelHelpers final |
3 | 3 | {
|
4 | 4 | enum
|
5 | 5 | {
|
@@ -98,66 +98,114 @@ struct DecibelHelpers
|
98 | 98 |
|
99 | 99 | //==============================================================================
|
100 | 100 | /** */
|
101 |
| -class Meter final : public Component |
| 101 | +class MeterModel |
102 | 102 | {
|
103 | 103 | public:
|
104 |
| - //============================================================================== |
105 | 104 | /** */
|
106 |
| - class Model |
107 |
| - { |
108 |
| - public: |
109 |
| - /** */ |
110 |
| - virtual ~Model() noexcept = default; |
| 105 | + MeterModel() noexcept = default; |
111 | 106 |
|
112 |
| - /** @returns the expiration time of the maximum meter level, after which it decays. */ |
113 |
| - virtual int64 getExpiryTimeMs() const noexcept { return 3000; } |
| 107 | + /** */ |
| 108 | + virtual ~MeterModel() noexcept = default; |
114 | 109 |
|
115 |
| - /** @returns */ |
116 |
| - virtual bool needsMaxLevel() const noexcept { return false; } |
| 110 | + //============================================================================== |
| 111 | + /** @returns the expiration time of the maximum meter level, after which it decays. */ |
| 112 | + virtual int64 getExpiryTimeMs() const noexcept { return 3000; } |
117 | 113 |
|
118 |
| - /** @returns */ |
119 |
| - virtual bool isHorizontal() const noexcept { return false; } |
| 114 | + /** @returns */ |
| 115 | + virtual bool needsMaxLevel() const noexcept { return false; } |
120 | 116 |
|
121 |
| - /** @returns */ |
122 |
| - virtual Array<float> getChannelLevels() const = 0; |
| 117 | + /** @returns */ |
| 118 | + virtual bool isHorizontal() const noexcept { return false; } |
123 | 119 |
|
124 |
| - /** */ |
125 |
| - struct ColourPosition final |
126 |
| - { |
127 |
| - ColourPosition() noexcept = default; |
| 120 | + //============================================================================== |
| 121 | + /** @returns an array where each index corresponds to a channel's peak |
| 122 | + (or whatever you've calculated). |
128 | 123 |
|
129 |
| - ColourPosition (Colour c, double d) noexcept : |
130 |
| - colour (c), |
131 |
| - decibels (d) |
132 |
| - { |
133 |
| - } |
| 124 | + In other words, the first index should be the left channel's peak, |
| 125 | + the next value should be for the right channel, and so on (as needed). |
| 126 | + */ |
| 127 | + virtual Array<float> getChannelLevels() const = 0; |
134 | 128 |
|
135 |
| - Colour colour; |
136 |
| - double decibels = 0.0; |
137 |
| - }; |
| 129 | + //============================================================================== |
| 130 | + /** */ |
| 131 | + struct ColourPosition final |
| 132 | + { |
| 133 | + ColourPosition() = default; |
| 134 | + ColourPosition (const ColourPosition&) = default; |
| 135 | + ColourPosition (ColourPosition&&) = default; |
| 136 | + ~ColourPosition() = default; |
| 137 | + ColourPosition& operator= (const ColourPosition&) = default; |
| 138 | + ColourPosition& operator= (ColourPosition&&) = default; |
| 139 | + |
| 140 | + ColourPosition (Colour c, double db) : |
| 141 | + colour (c), |
| 142 | + decibels (db) |
| 143 | + { |
| 144 | + } |
138 | 145 |
|
139 |
| - /** @returns */ |
140 |
| - virtual std::array<ColourPosition, 3> getColourPositions() const |
| 146 | + void addToGradient (ColourGradient& destination, |
| 147 | + bool isHorizontal) |
141 | 148 | {
|
142 |
| - return |
143 |
| - { |
144 |
| - ColourPosition (Colours::red, 0.0), |
145 |
| - ColourPosition (Colours::yellow, -9.0), |
146 |
| - ColourPosition (Colours::green, -18.0) |
147 |
| - }; |
| 149 | + const auto v = DecibelHelpers::decibelsToMeterProportion (decibels); |
| 150 | + |
| 151 | + if (isHorizontal) |
| 152 | + destination.addColour (v, colour); |
| 153 | + else |
| 154 | + destination.addColour (1.0 - v, colour); |
148 | 155 | }
|
| 156 | + |
| 157 | + Colour colour = Colours::black; |
| 158 | + double decibels = 0.0; |
149 | 159 | };
|
150 | 160 |
|
| 161 | + /** @returns */ |
| 162 | + virtual std::vector<ColourPosition> getColourPositions() const |
| 163 | + { |
| 164 | + return |
| 165 | + { |
| 166 | + { Colours::red, 0.0 }, |
| 167 | + { Colours::yellow, -9.0 }, |
| 168 | + { Colours::green, -18.0 } |
| 169 | + }; |
| 170 | + } |
| 171 | + |
| 172 | +private: |
| 173 | + #if ! JUCE_DISABLE_ASSERTIONS |
| 174 | + friend class Meter; |
| 175 | + struct Empty {}; |
| 176 | + std::shared_ptr<Empty> sharedState = std::make_shared<Empty>(); |
| 177 | + #endif |
| 178 | +}; |
| 179 | + |
| 180 | +//============================================================================== |
| 181 | +/** */ |
| 182 | +class Meter final : public Component |
| 183 | +{ |
| 184 | +public: |
| 185 | + |
151 | 186 | //==============================================================================
|
152 | 187 | /** */
|
153 |
| - Meter (Model* model_ = nullptr); |
| 188 | + Meter (MeterModel* meterModel = nullptr); |
154 | 189 |
|
155 | 190 | //==============================================================================
|
156 |
| - /** */ |
157 |
| - void setModel (Model*); |
| 191 | + /** Changes the current data model to display. |
158 | 192 |
|
159 |
| - /** */ |
160 |
| - Model* getModel() const noexcept { return model; } |
| 193 | + The MeterModel instance must stay alive for as long as the Meter |
| 194 | + holds a pointer to it. Be careful to destroy the Meter before the |
| 195 | + MeterModel, or to call Meter::setMeterModel (nullptr) before destroying |
| 196 | + the MeterModel. |
| 197 | + */ |
| 198 | + void setMeterModel (MeterModel*); |
| 199 | + |
| 200 | + /** Returns the current list model. */ |
| 201 | + MeterModel* getMeterModel() const noexcept |
| 202 | + { |
| 203 | + #if ! JUCE_DISABLE_ASSERTIONS |
| 204 | + checkModelPtrIsValid(); |
| 205 | + #endif |
| 206 | + |
| 207 | + return model; |
| 208 | + } |
161 | 209 |
|
162 | 210 | //==============================================================================
|
163 | 211 | /** @returns true if the levels have changed. */
|
@@ -193,10 +241,10 @@ class Meter final : public Component
|
193 | 241 | const Rectangle<int>& getMeterArea() const noexcept { return meterArea; }
|
194 | 242 |
|
195 | 243 | private:
|
196 |
| - float level = 0.0f; // The last measured audio absolute volume level. |
197 |
| - float lastLevel = 0.0f; // The volume level of the last update, used to check if levels have changed for repainting. |
198 |
| - float maxLevel = 0.0f; // The maximum audio levels of the trailing 3 seconds. |
199 |
| - float lastMaxLevel = 0.0f; // The max volume level of the last update. |
| 244 | + float level = 0.0f, // The last measured audio absolute volume level. |
| 245 | + lastLevel = 0.0f, // The volume level of the last update, used to check if levels have changed for repainting. |
| 246 | + maxLevel = 0.0f, // The maximum audio levels of the trailing 3 seconds. |
| 247 | + lastMaxLevel = 0.0f; // The max volume level of the last update. |
200 | 248 | int64 timeOfMaximumMs = 0; // The time of the last maximum audio level.
|
201 | 249 | Rectangle<int> meterArea; // The left/right drawable regions for the meter.
|
202 | 250 |
|
@@ -236,10 +284,18 @@ class Meter final : public Component
|
236 | 284 |
|
237 | 285 | private:
|
238 | 286 | //==============================================================================
|
239 |
| - Model* model = nullptr; |
| 287 | + MeterModel* model = nullptr; |
240 | 288 | Array<ChannelContext> channels;
|
241 | 289 | Array<float> levels;
|
242 | 290 | ClippingLevel clippingLevel = ClippingLevel::none;
|
| 291 | + ColourGradient gradient; |
| 292 | + |
| 293 | + void assignModelPtr (MeterModel*); |
| 294 | + |
| 295 | + #if ! JUCE_DISABLE_ASSERTIONS |
| 296 | + std::weak_ptr<MeterModel::Empty> weakModelPtr; |
| 297 | + void checkModelPtrIsValid() const; |
| 298 | + #endif |
243 | 299 |
|
244 | 300 | //==============================================================================
|
245 | 301 | void updateClippingLevel (bool timeToUpdate);
|
|
0 commit comments