FastLED 3.10.6
Loading...
Searching...
No Matches

◆ render()

void mood_ring::GrooveLayer::render ( const RoomSense & s,
const MoodPalette & pal,
float dtSec,
RingCanvas & c )

Definition at line 165 of file layers.cpp.

166 {
167 const float dtMs = dtSec * 1000.0f;
168 const float slot = 1.0f / static_cast<float>(kMarkers);
169 const float beatsPerSec = (s.bpm > 20.0f) ? s.bpm / 60.0f : 0.0f;
170
171 // Rotate continuously at tempo: one slot per beat. On each beat, measure
172 // how far the nearest marker is from its slot boundary and slew that
173 // error out over the next fraction of a beat, so the geometry locks to
174 // the music without ever jumping.
175 mRotation = advance(mRotation, slot * beatsPerSec, dtSec);
176 if (s.events.beat) {
177 const float inSlot =
178 mRotation -
179 slot * static_cast<float>(static_cast<int>(mRotation / slot));
180 // Nearest boundary: either back to the start of this slot or forward
181 // to the next one.
182 const float err = (inSlot < slot * 0.5f) ? -inSlot : (slot - inSlot);
183 mCorrection = err;
184 }
185 if (mCorrection != 0.0f && beatsPerSec > 0.0f) {
186 const float maxStep = slot * beatsPerSec * dtSec / kCorrectionBeats;
187 float step = mCorrection;
188 if (step > maxStep)
189 step = maxStep;
190 if (step < -maxStep)
191 step = -maxStep;
192 mRotation = wrapTurns(mRotation + step);
193 mCorrection -= step;
195 mCorrection = 0.0f;
196 }
197
198 // Swell into the beat: brightness climbs over the last quarter of the
199 // beat and releases right after it. Followed so a beat that lands early
200 // (phase reset from 0.9 to 0) ramps over a frame instead of stepping.
201 float swellTarget;
202 if (s.beatPhase >= kSwellStart) {
203 swellTarget = smoothstepf(kSwellStart, 1.0f, s.beatPhase);
204 } else {
205 swellTarget = 1.0f - smoothstepf(0.0f, 0.25f, s.beatPhase);
206 }
207 const float swell =
208 mSwell.update(swellTarget, dtMs, kSwellAttackMs, kSwellReleaseMs);
209
210 // Event envelopes: instant-feeling attack, smooth release.
211 const float flash = mFlash.update(s.events.downbeat ? 1.0f : 0.0f, dtMs,
212 kFlashAttackMs, kFlashReleaseMs);
213 const float snare = mSnare.update(s.events.snare ? 1.0f : 0.0f, dtMs,
214 kSnareAttackMs, kSnareReleaseMs);
215
216 const float markerGain = 0.45f + 0.55f * swell + 0.5f * s.punch;
217
218 // Bed: measure phase breathes the whole ring once per bar.
219 const float bar = 0.5f + 0.5f * fl::sin(kTwoPi * s.measurePhase);
220 c.fill(paletteColor(pal, 0.2f), 0.08f + 0.12f * bar);
221
222 // Markers alternate ends of the palette so the geometry reads.
223 for (int k = 0; k < kMarkers; ++k) {
224 const float pos = wrapTurns(mRotation + slot * static_cast<float>(k));
225 const float t = (k & 1) ? 1.0f : 0.4f;
226 c.lobe(pos, kMarkerSigma + 0.02f * s.low, paletteColor(pal, t),
227 markerGain);
228 }
229
230 // Snare: a thin counter-marker halfway between each pair.
231 if (snare > 0.001f) {
232 for (int k = 0; k < kMarkers; ++k) {
233 const float pos =
234 wrapTurns(mRotation + slot * (static_cast<float>(k) + 0.5f));
235 c.lobe(pos, kMarkerSigma * 0.6f, rgbfWhite(), 0.6f * snare);
236 }
237 }
238
239 // Downbeat: whole-ring flash in the warm end of the family.
240 if (flash > 0.001f) {
241 c.fill(paletteColor(pal, 1.0f), 0.5f * flash);
242 }
243}
uint8_t pos
Definition Blur.ino:11
float mCorrection
remaining phase correction, turns
Definition layers.h:78
float mRotation
turns, advanced continuously
Definition layers.h:77
static const int kMarkers
Definition layers.h:75
static uint32_t t
Definition Luminova.h:55
enable_if< is_fixed_point< T >::value, T >::type sin(T angle) FL_NO_EXCEPT
constexpr enable_if< is_fixed_point< T >::value, T >::type step(T edge, T x) FL_NO_EXCEPT
float advance(float pos, float turnsPerSec, float dtSec)
Definition layers.cpp:43
float smoothstepf(float e0, float e1, float x)
0 below e0, 1 above e1, smooth in between.
RGBf rgbfWhite()
Definition ring_canvas.h:36
RGBf paletteColor(const MoodPalette &p, float t)
Colour at position t in [0, 1] along the family, at value v.
float wrapTurns(float t)
Wrap into [0, 1).

References mood_ring::RoomEvents::beat, mood_ring::RoomSense::beatPhase, mood_ring::RoomSense::bpm, mood_ring::RoomEvents::downbeat, mood_ring::RoomSense::events, mood_ring::RingCanvas::fill(), kMarkers, mood_ring::RingCanvas::lobe(), mood_ring::RoomSense::low, mCorrection, mood_ring::RoomSense::measurePhase, mFlash, mRotation, mSnare, mSwell, mood_ring::paletteColor(), pos, mood_ring::RoomSense::punch, mood_ring::rgbfWhite(), fl::sin(), mood_ring::smoothstepf(), mood_ring::RoomEvents::snare, t, and mood_ring::wrapTurns().

+ Here is the call graph for this function: