1
// candle-mode.c: Candle mode for Anduril.
2
// Copyright (C) 2017-2023 Selene ToyKeeper
3
// SPDX-License-Identifier: GPL-3.0-or-later
6
#include "candle-mode.h"
8
#ifdef USE_SUNSET_TIMER
9
#include "sunset-timer.h"
12
uint8_t candle_mode_state(Event event, uint16_t arg) {
13
static int8_t ramp_direction = 1;
14
#define MAX_CANDLE_LEVEL (MAX_LEVEL-CANDLE_AMPLITUDE-15)
15
static uint8_t candle_wave1 = 0;
16
static uint8_t candle_wave2 = 0;
17
static uint8_t candle_wave3 = 0;
18
static uint8_t candle_wave2_speed = 0;
19
// these should add up to 100
20
#define CANDLE_WAVE1_MAXDEPTH 30
21
#define CANDLE_WAVE2_MAXDEPTH 45
22
#define CANDLE_WAVE3_MAXDEPTH 25
23
static const uint8_t candle_wave1_depth = CANDLE_WAVE1_MAXDEPTH * CANDLE_AMPLITUDE / 100;
24
static uint8_t candle_wave2_depth = CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100;
25
static uint8_t candle_wave3_depth = CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100;
26
static uint8_t candle_mode_brightness = 24;
28
#ifdef USE_SUNSET_TIMER
29
// let the candle "burn out" and shut itself off
30
// if the user told it to
31
// cache this in case it changes when the timer is called
32
uint8_t sunset_active = sunset_timer;
34
sunset_timer_state(event, arg);
35
// if the timer just expired, shut off
36
if (sunset_active && (! sunset_timer)) {
37
set_state(off_state, 0);
40
#endif // ifdef USE_SUNSET_TIMER
43
if (event == EV_enter_state) {
47
#ifdef USE_SUNSET_TIMER
48
// 2 clicks: cancel timer
49
else if (event == EV_2clicks) {
50
// parent state just rotated through strobe/flasher modes,
51
// so cancel timer... in case any time was left over from earlier
55
#endif // ifdef USE_SUNSET_TIMER
56
// hold: change brightness (brighter)
57
else if (event == EV_click1_hold) {
58
// ramp away from extremes
60
if (candle_mode_brightness >= MAX_CANDLE_LEVEL) { ramp_direction = -1; }
61
else if (candle_mode_brightness <= 1) { ramp_direction = 1; }
63
// change brightness, but not too far
64
candle_mode_brightness += ramp_direction;
65
if (candle_mode_brightness < 1) candle_mode_brightness = 1;
66
else if (candle_mode_brightness > MAX_CANDLE_LEVEL) candle_mode_brightness = MAX_CANDLE_LEVEL;
69
// reverse ramp direction on hold release
70
else if (event == EV_click1_hold_release) {
71
ramp_direction = -ramp_direction;
74
// click, hold: change brightness (dimmer)
75
else if (event == EV_click2_hold) {
77
if (candle_mode_brightness > 1)
78
candle_mode_brightness --;
81
// clock tick: animate candle brightness
82
else if (event == EV_tick) {
83
// un-reverse after 1 second
84
if (arg == AUTO_REVERSE_TIME) ramp_direction = 1;
86
// 3-oscillator synth for a relatively organic pattern
88
add = ((triangle_wave(candle_wave1) * candle_wave1_depth) >> 8)
89
+ ((triangle_wave(candle_wave2) * candle_wave2_depth) >> 8)
90
+ ((triangle_wave(candle_wave3) * candle_wave3_depth) >> 8);
91
uint16_t brightness = candle_mode_brightness + add;
93
// self-timer dims the light during the final minute
94
#ifdef USE_SUNSET_TIMER
95
if (1 == sunset_timer) {
96
brightness = brightness
97
* ((TICKS_PER_MINUTE>>5) - (sunset_ticks>>5))
98
/ (TICKS_PER_MINUTE>>5);
100
#endif // ifdef USE_SUNSET_TIMER
102
set_level(brightness);
104
// wave1: slow random LFO
105
// TODO: make wave slower and more erratic?
106
if ((arg & 1) == 0) candle_wave1 += pseudo_rand() & 1;
107
// wave2: medium-speed erratic LFO
108
candle_wave2 += candle_wave2_speed;
109
// wave3: erratic fast wave
110
candle_wave3 += pseudo_rand() % 37;
111
// S&H on wave2 frequency to make it more erratic
112
if ((pseudo_rand() & 0b00111111) == 0)
113
candle_wave2_speed = pseudo_rand() % 13;
114
// downward sawtooth on wave2 depth to simulate stabilizing
115
if ((candle_wave2_depth > 0) && ((pseudo_rand() & 0b00111111) == 0))
116
candle_wave2_depth --;
117
// random sawtooth retrigger
118
if (pseudo_rand() == 0) {
120
//candle_wave2_depth = 2 + (pseudo_rand() % ((CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100) - 2));
121
candle_wave2_depth = pseudo_rand() % (CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100);
122
//candle_wave3_depth = 5;
125
// downward sawtooth on wave3 depth to simulate stabilizing
126
if ((candle_wave3_depth > 2) && ((pseudo_rand() & 0b00011111) == 0))
127
candle_wave3_depth --;
128
if ((pseudo_rand() & 0b01111111) == 0)
130
//candle_wave3_depth = 2 + (pseudo_rand() % ((CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100) - 2));
131
candle_wave3_depth = pseudo_rand() % (CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100);
132
return EVENT_HANDLED;
134
return EVENT_NOT_HANDLED;