1 // Copyright 2014 Emilie Gillet.
2 //
3 // Author: Emilie Gillet (emilie.o.gillet@gmail.com)
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 // THE SOFTWARE.
22 //
23 // See http://creativecommons.org/licenses/MIT/ for more information.
24 //
25 // -----------------------------------------------------------------------------
26 //
27 // User interface.
28
29 #include "warps/ui.h"
30
31 #include <algorithm>
32
33 #include "stmlib/system/system_clock.h"
34 #include "stmlib/dsp/units.h"
35
36 #include "warps/cv_scaler.h"
37
38 namespace warps {
39
40 using namespace std;
41 using namespace stmlib;
42
43 /* static */
44 const uint8_t Ui::palette_[10][3] = {
45 { 0, 192, 64 },
46 { 64, 255, 0 },
47 { 255, 255, 0 },
48 { 255, 64, 0 },
49 { 255, 0, 0 },
50 { 255, 0, 64 },
51 { 255, 0, 255 },
52 { 0, 0, 255 },
53 { 0, 255, 192 },
54 { 0, 255, 192 },
55 };
56
57 /* static */
58 const uint8_t Ui::easter_egg_palette_[10][3] = {
59 { 0, 0, 64 },
60 { 0, 0, 255 },
61 { 0, 255, 192 },
62 { 0, 192, 64 },
63 { 64, 255, 0 },
64 { 255, 255, 0 },
65 { 255, 192, 0 },
66 { 255, 64, 0 },
67 { 255, 0, 0 },
68 { 255, 0, 0 },
69 };
70
Init(Settings * settings,CvScaler * cv_scaler,Modulator * modulator)71 void Ui::Init(Settings* settings, CvScaler* cv_scaler, Modulator* modulator) {
72 leds_.Init();
73 switches_.Init();
74
75 mode_ = UI_MODE_NORMAL;
76 settings_ = settings;
77 cv_scaler_ = cv_scaler;
78 modulator_ = modulator;
79
80 modulator_->set_easter_egg(settings_->state().boot_in_easter_egg_mode);
81 carrier_shape_ = settings_->state().carrier_shape;
82 UpdateCarrierShape();
83 }
84
UpdateCarrierShape()85 void Ui::UpdateCarrierShape() {
86 modulator_->mutable_parameters()->carrier_shape = carrier_shape_;
87 settings_->mutable_state()->carrier_shape = carrier_shape_;
88 }
89
Poll()90 void Ui::Poll() {
91 // Called at 1.6kHz instead of 1kHz, so the "milliseconds" clock actually runs
92 // 1.6x faster. Not a big deal since it is used only for controlling LED
93 // blinking rate and detecting long button presses.
94 system_clock.Tick();
95 switches_.Debounce();
96 if (switches_.just_pressed(0)) {
97 queue_.AddEvent(CONTROL_SWITCH, 0, 0);
98 press_time_ = system_clock.milliseconds();
99 }
100
101 if (switches_.pressed(0) && press_time_) {
102 if (cv_scaler_->ready_for_calibration() && (system_clock.milliseconds() - press_time_) >= 4800) {
103 queue_.AddEvent(CONTROL_SWITCH, 1, 0);
104 press_time_ = 0;
105 } else if ((system_clock.milliseconds() - press_time_) >= 9600) {
106 queue_.AddEvent(CONTROL_SWITCH, 2, 0);
107 press_time_ = 0;
108 }
109 }
110
111 if (switches_.released(0) && press_time_) {
112 queue_.AddEvent(
113 CONTROL_SWITCH,
114 0,
115 system_clock.milliseconds() - press_time_ + 1);
116 }
117
118 bool blink = (system_clock.milliseconds() & 127) > 64;
119 bool slow_blink = (system_clock.milliseconds() & 255) > 128;
120 switch (mode_) {
121 case UI_MODE_NORMAL:
122 {
123 uint8_t rgb[3];
124 float zone;
125 const Parameters& p = modulator_->parameters();
126 const uint8_t (*palette)[3];
127 if (modulator_->easter_egg()) {
128 zone = p.phase_shift;
129 palette = easter_egg_palette_;
130 } else {
131 zone = p.modulation_algorithm;
132 palette = palette_;
133 }
134 zone *= 8.0f;
135 MAKE_INTEGRAL_FRACTIONAL(zone);
136 int32_t zone_fractional_i = static_cast<int32_t>(
137 zone_fractional * 256.0f);
138 for (int32_t i = 0; i < 3; ++i) {
139 int32_t a = palette[zone_integral][i];
140 int32_t b = palette[zone_integral + 1][i];
141 rgb[i] = a + ((b - a) * zone_fractional_i >> 8);
142 }
143 leds_.set_main(rgb[0], rgb[1], rgb[2]);
144 leds_.set_osc(
145 carrier_shape_ >= 2 ? 255 : 0,
146 carrier_shape_ > 0 && carrier_shape_ <= 2 ? 255 : 0);
147 }
148 break;
149
150 case UI_MODE_CALIBRATION_C1:
151 leds_.set_main(0, blink ? 255 : 0, blink ? 64 : 0);
152 leds_.set_osc(blink ? 255 : 0, blink ? 255 : 0);
153 break;
154
155 case UI_MODE_CALIBRATION_C3:
156 leds_.set_main(blink ? 255 : 0, 0, blink ? 32 : 0);
157 leds_.set_osc(blink ? 255 : 0, 0);
158 break;
159
160 case UI_MODE_CALIBRATION_LOW:
161 leds_.set_main(slow_blink ? 255 : 0, 0, 0);
162 leds_.set_osc(slow_blink ? 255 : 0, 0);
163 break;
164
165 case UI_MODE_CALIBRATION_HIGH:
166 leds_.set_main(0, slow_blink ? 255 : 0, 0);
167 leds_.set_osc(0, slow_blink ? 255 : 0);
168 break;
169
170 case UI_MODE_PANIC:
171 case UI_MODE_CALIBRATION_ERROR:
172 leds_.set_osc(blink ? 255 : 0, 0);
173 leds_.set_main(blink ? 255 : 0, 0, 0);
174 break;
175
176 case UI_MODE_EASTER_EGG_DANCE:
177 {
178 leds_.set_osc(0, blink ? 255 : 0);
179 uint8_t color = (system_clock.milliseconds() >> 9) % 9;
180 leds_.set_main(
181 easter_egg_palette_[color][0],
182 easter_egg_palette_[color][1],
183 easter_egg_palette_[color][2]);
184 }
185 break;
186 }
187
188 if (modulator_->bypass()) {
189 uint16_t red = system_clock.milliseconds() & 4095;
190 uint16_t green = (system_clock.milliseconds() + 1333) & 4095;
191 uint16_t blue = (system_clock.milliseconds() + 2667) & 4095;
192 green = green < 2048 ? green : 4095 - green;
193 red = red < 2048 ? red : 4095 - red;
194 blue = blue < 2048 ? blue : 4095 - blue;
195 leds_.set_osc(255, 255);
196 leds_.set_main(red >> 3, green >> 3, blue >> 3);
197 }
198 leds_.Write();
199 }
200
DetectSecretHandshake()201 bool Ui::DetectSecretHandshake() {
202 for (int32_t i = 0; i < 5; ++i) {
203 secret_handshake_[i] = secret_handshake_[i + 1];
204 }
205 secret_handshake_[5] = cv_scaler_->easter_egg_digit();
206 uint8_t expected[6] = { 2, 4, 3, 6, 1, 5 };
207 return equal(
208 &secret_handshake_[0],
209 &secret_handshake_[6],
210 &expected[0]);
211 }
212
OnSwitchPressed(const Event & e)213 void Ui::OnSwitchPressed(const Event& e) {
214 switch (e.control_id) {
215 case 0:
216 switch (mode_) {
217 case UI_MODE_CALIBRATION_C1:
218 CalibrateC1();
219 break;
220 case UI_MODE_CALIBRATION_C3:
221 CalibrateC3();
222 break;
223 case UI_MODE_CALIBRATION_LOW:
224 CalibrateLow();
225 break;
226 case UI_MODE_CALIBRATION_HIGH:
227 CalibrateHigh();
228 break;
229 default:
230 if (!DetectSecretHandshake()) {
231 carrier_shape_ = (carrier_shape_ + 1) & 3;
232 } else {
233 bool easter = !modulator_->easter_egg();
234 modulator_->set_easter_egg(easter);
235 settings_->mutable_state()->boot_in_easter_egg_mode = easter;
236 carrier_shape_ = 1;
237 mode_ = UI_MODE_EASTER_EGG_DANCE;
238 }
239 UpdateCarrierShape();
240 settings_->Save();
241 break;
242 }
243 break;
244
245 case 1:
246 StartCalibration();
247 break;
248
249 case 2:
250 StartNormalizationCalibration();
251 break;
252
253 default:
254 break;
255 }
256 }
257
OnSwitchReleased(const Event & e)258 void Ui::OnSwitchReleased(const Event& e) {
259
260 }
261
StartCalibration()262 void Ui::StartCalibration() {
263 cv_scaler_->StartCalibration();
264 mode_ = UI_MODE_CALIBRATION_C1;
265 }
266
CalibrateC1()267 void Ui::CalibrateC1() {
268 cv_scaler_->CalibrateC1();
269 cv_scaler_->CalibrateOffsets();
270 mode_ = UI_MODE_CALIBRATION_C3;
271 }
272
CalibrateC3()273 void Ui::CalibrateC3() {
274 if (cv_scaler_->CalibrateC3()) {
275 settings_->Save();
276 mode_ = UI_MODE_NORMAL;
277 } else {
278 mode_ = UI_MODE_CALIBRATION_ERROR;
279 }
280 }
281
StartNormalizationCalibration()282 void Ui::StartNormalizationCalibration() {
283 cv_scaler_->StartNormalizationCalibration();
284 mode_ = UI_MODE_CALIBRATION_LOW;
285 }
286
CalibrateLow()287 void Ui::CalibrateLow() {
288 cv_scaler_->CalibrateLow();
289 mode_ = UI_MODE_CALIBRATION_HIGH;
290 }
291
CalibrateHigh()292 void Ui::CalibrateHigh() {
293 if (cv_scaler_->CalibrateHigh()) {
294 settings_->Save();
295 mode_ = UI_MODE_NORMAL;
296 } else {
297 mode_ = UI_MODE_CALIBRATION_ERROR;
298 }
299
300 }
301
DoEvents()302 void Ui::DoEvents() {
303 while (queue_.available()) {
304 Event e = queue_.PullEvent();
305 if (e.control_type == CONTROL_SWITCH) {
306 if (e.data == 0) {
307 OnSwitchPressed(e);
308 } else {
309 OnSwitchReleased(e);
310 }
311 }
312 }
313 if (mode_ == UI_MODE_EASTER_EGG_DANCE || mode_ == UI_MODE_CALIBRATION_ERROR) {
314 if (queue_.idle_time() > 6000) {
315 mode_ = UI_MODE_NORMAL;
316 }
317 } else {
318 if (queue_.idle_time() > 1000) {
319 queue_.Touch();
320 }
321 }
322 }
323
HandleFactoryTestingRequest(uint8_t command)324 uint8_t Ui::HandleFactoryTestingRequest(uint8_t command) {
325 uint8_t argument = command & 0x1f;
326 command = command >> 5;
327 uint8_t reply = 0;
328 switch (command) {
329 case FACTORY_TESTING_READ_POT:
330 case FACTORY_TESTING_READ_CV:
331 reply = cv_scaler_->adc_value(argument);
332 break;
333
334 case FACTORY_TESTING_READ_NORMALIZATION:
335 reply = cv_scaler_->normalization(argument);
336 break;
337
338 case FACTORY_TESTING_READ_GATE:
339 return switches_.pressed(argument);
340 break;
341
342 case FACTORY_TESTING_SET_BYPASS:
343 modulator_->set_bypass(argument);
344 break;
345
346 case FACTORY_TESTING_CALIBRATE:
347 {
348 switch (argument) {
349 case 0:
350 StartCalibration();
351 break;
352
353 case 1:
354 CalibrateC1();
355 break;
356
357 case 2:
358 CalibrateC3();
359 break;
360
361 case 3:
362 StartNormalizationCalibration();
363 break;
364
365 case 4:
366 CalibrateLow();
367 break;
368
369 case 5:
370 CalibrateHigh();
371 carrier_shape_ = 0;
372 UpdateCarrierShape();
373 break;
374 }
375 }
376 break;
377 }
378 return reply;
379 }
380
381 } // namespace warps
382