1 // Copyright 2015 Olivier Gillet.
2 //
3 // Author: Olivier Gillet (ol.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 "rings/ui.h"
30 
31 #include <algorithm>
32 
33 #include "stmlib/system/system_clock.h"
34 
35 #include "rings/cv_scaler.h"
36 #include "rings/dsp/part.h"
37 #include "rings/dsp/string_synth_part.h"
38 
39 namespace rings {
40 
41 const int32_t kLongPressDuration = 3000;
42 
43 using namespace std;
44 using namespace stmlib;
45 
Init(Settings * settings,CvScaler * cv_scaler,Part * part,StringSynthPart * string_synth)46 void Ui::Init(
47     Settings* settings,
48     CvScaler* cv_scaler,
49     Part* part,
50     StringSynthPart* string_synth) {
51   leds_.Init();
52   switches_.Init();
53 
54   settings_ = settings;
55   cv_scaler_ = cv_scaler;
56   part_ = part;
57   string_synth_ = string_synth;
58 
59   part_->set_polyphony(settings_->state().polyphony);
60   part_->set_model(static_cast<ResonatorModel>(settings_->state().model));
61   string_synth_->set_polyphony(settings_->state().polyphony);
62   string_synth_->set_fx(static_cast<FxType>(settings_->state().model));
63   mode_ = settings_->state().easter_egg
64       ? UI_MODE_EASTER_EGG_INTRO
65       : UI_MODE_NORMAL;
66 }
67 
SaveState()68 void Ui::SaveState() {
69   settings_->mutable_state()->polyphony = part_->polyphony();
70   settings_->mutable_state()->model = part_->model();
71   settings_->Save();
72 }
73 
AnimateEasterEggLeds()74 void Ui::AnimateEasterEggLeds() {
75   mode_ = settings_->state().easter_egg
76       ? UI_MODE_EASTER_EGG_INTRO
77       : UI_MODE_EASTER_EGG_OUTRO;
78 }
79 
Poll()80 void Ui::Poll() {
81   // 1kHz.
82   system_clock.Tick();
83   switches_.Debounce();
84 
85   for (uint8_t i = 0; i < kNumSwitches; ++i) {
86     if (switches_.just_pressed(i)) {
87       queue_.AddEvent(CONTROL_SWITCH, i, 0);
88       press_time_[i] = system_clock.milliseconds();
89     }
90     if (switches_.pressed(i) && press_time_[i] != 0) {
91       int32_t pressed_time = system_clock.milliseconds() - press_time_[i];
92       if (pressed_time > kLongPressDuration) {
93         queue_.AddEvent(CONTROL_SWITCH, i, pressed_time);
94         press_time_[i] = 0;
95       }
96     }
97     if (switches_.released(i) && press_time_[i] != 0) {
98       queue_.AddEvent(
99           CONTROL_SWITCH,
100           i,
101           system_clock.milliseconds() - press_time_[i] + 1);
102       press_time_[i] = 0;
103     }
104   }
105 
106   bool blink = (system_clock.milliseconds() & 127) > 64;
107   bool slow_blink = (system_clock.milliseconds() & 255) > 128;
108   switch (mode_) {
109     case UI_MODE_NORMAL:
110       {
111         uint8_t pwm_counter = system_clock.milliseconds() & 15;
112         uint8_t triangle = (system_clock.milliseconds() >> 5) & 31;
113         triangle = triangle < 16 ? triangle : 31 - triangle;
114         leds_.set(0, part_->polyphony() >= 2, part_->polyphony() <= 2);
115         leds_.set(1, part_->model() >= 1, part_->model() <= 1);
116         // Fancy modes!
117         if (part_->polyphony() == 3) {
118           leds_.set(0, true, pwm_counter < triangle);
119         }
120         if (part_->model() >= 3) {
121           bool led_1 = part_->model() >= 4 && pwm_counter < triangle;
122           bool led_2 = part_->model() <= 4 && pwm_counter < triangle;
123           leds_.set(1, led_1, led_2);
124         }
125         ++strumming_flag_interval_;
126         if (strumming_flag_counter_) {
127           --strumming_flag_counter_;
128           leds_.set(0, false, false);
129         }
130       }
131       break;
132 
133     case UI_MODE_CALIBRATION_C1:
134       leds_.set(0, blink, blink);
135       leds_.set(1, false, false);
136       break;
137 
138     case UI_MODE_CALIBRATION_C3:
139       leds_.set(0, false, false);
140       leds_.set(1, blink, blink);
141       break;
142 
143     case UI_MODE_CALIBRATION_LOW:
144       leds_.set(0, slow_blink, 0);
145       leds_.set(1, slow_blink, 0);
146       break;
147 
148     case UI_MODE_CALIBRATION_HIGH:
149       leds_.set(0, false, slow_blink);
150       leds_.set(1, false, slow_blink);
151       break;
152 
153     case UI_MODE_EASTER_EGG_INTRO:
154       {
155         uint8_t pwm_counter = system_clock.milliseconds() & 15;
156         uint8_t triangle_1 = (system_clock.milliseconds() / 7) & 31;
157         uint8_t triangle_2 = (system_clock.milliseconds() / 17) & 31;
158         triangle_1 = triangle_1 < 16 ? triangle_1 : 31 - triangle_1;
159         triangle_2 = triangle_2 < 16 ? triangle_2 : 31 - triangle_2;
160         leds_.set(
161             0,
162             triangle_1 > pwm_counter,
163             triangle_2 > pwm_counter);
164         leds_.set(
165             1,
166             triangle_2 > pwm_counter,
167             triangle_1 > pwm_counter);
168       }
169       break;
170 
171     case UI_MODE_EASTER_EGG_OUTRO:
172       {
173         uint8_t pwm_counter = 7;
174         uint8_t triangle_1 = (system_clock.milliseconds() / 9) & 31;
175         uint8_t triangle_2 = (system_clock.milliseconds() / 13) & 31;
176         triangle_1 = triangle_1 < 16 ? triangle_1 : 31 - triangle_1;
177         triangle_2 = triangle_2 < 16 ? triangle_2 : 31 - triangle_2;
178         leds_.set(0, triangle_1 < pwm_counter, triangle_1 > pwm_counter);
179         leds_.set(1, triangle_2 > pwm_counter, triangle_2 < pwm_counter);
180       }
181       break;
182 
183     case UI_MODE_PANIC:
184       leds_.set(0, blink, false);
185       leds_.set(1, blink, false);
186       break;
187   }
188   leds_.Write();
189 }
190 
FlushEvents()191 void Ui::FlushEvents() {
192   queue_.Flush();
193 }
194 
OnSwitchPressed(const Event & e)195 void Ui::OnSwitchPressed(const Event& e) {
196 
197 }
198 
OnSwitchReleased(const Event & e)199 void Ui::OnSwitchReleased(const Event& e) {
200   // Check if the other switch is still pressed.
201   if (switches_.pressed(1 - e.control_id)) {
202     if (mode_ == UI_MODE_CALIBRATION_C1) {
203       StartNormalizationCalibration();
204     } else {
205       StartCalibration();
206     }
207     press_time_[0] = press_time_[1] = 0;
208     return;
209   }
210 
211   switch (e.control_id) {
212     case 0:
213       if (e.data >= kLongPressDuration) {
214         if (cv_scaler_->easter_egg()) {
215           settings_->ToggleEasterEgg();
216           AnimateEasterEggLeds();
217         } else {
218           part_->set_polyphony(3);
219           string_synth_->set_polyphony(3);
220         }
221         SaveState();
222       } else {
223         switch (mode_) {
224           case UI_MODE_CALIBRATION_C1:
225             CalibrateC1();
226             break;
227           case UI_MODE_CALIBRATION_C3:
228             CalibrateC3();
229             break;
230           case UI_MODE_CALIBRATION_LOW:
231             CalibrateLow();
232             break;
233           case UI_MODE_CALIBRATION_HIGH:
234             CalibrateHigh();
235             break;
236           default:
237             {
238               int32_t polyphony = part_->polyphony();
239               if (polyphony == 3) {
240                 polyphony = 2;
241               }
242               polyphony <<= 1;
243               if (polyphony > 4) {
244                 polyphony = 1;
245               }
246               part_->set_polyphony(polyphony);
247               string_synth_->set_polyphony(polyphony);
248               SaveState();
249             }
250             break;
251           }
252       }
253       break;
254 
255     case 1:
256       if (e.data >= kLongPressDuration) {
257         if (cv_scaler_->easter_egg()) {
258           settings_->ToggleEasterEgg();
259           AnimateEasterEggLeds();
260         } else {
261           int32_t model = part_->model();
262           if (model >= 3) {
263             model -= 3;
264           } else {
265             model += 3;
266           }
267           part_->set_model(static_cast<ResonatorModel>(model));
268           string_synth_->set_fx(static_cast<FxType>(model));
269         }
270       } else {
271         int32_t model = part_->model();
272         if (model >= 3) {
273           model -= 3;
274         } else {
275           model = (model + 1) % 3;
276         }
277         part_->set_model(static_cast<ResonatorModel>(model));
278         string_synth_->set_fx(static_cast<FxType>(model));
279       }
280       SaveState();
281       break;
282 
283     default:
284       break;
285   }
286 }
287 
StartCalibration()288 void Ui::StartCalibration() {
289   mode_ = UI_MODE_CALIBRATION_C1;
290 }
291 
CalibrateC1()292 void Ui::CalibrateC1() {
293   cv_scaler_->CalibrateC1();
294   cv_scaler_->CalibrateOffsets();
295   mode_ = UI_MODE_CALIBRATION_C3;
296 }
297 
CalibrateC3()298 void Ui::CalibrateC3() {
299   bool success = cv_scaler_->CalibrateC3();
300   if (success) {
301     settings_->Save();
302     mode_ = UI_MODE_NORMAL;
303   } else {
304     mode_ = UI_MODE_PANIC;
305   }
306 }
307 
StartNormalizationCalibration()308 void Ui::StartNormalizationCalibration() {
309   cv_scaler_->StartNormalizationCalibration();
310   mode_ = UI_MODE_CALIBRATION_LOW;
311 }
312 
CalibrateLow()313 void Ui::CalibrateLow() {
314   cv_scaler_->CalibrateLow();
315   mode_ = UI_MODE_CALIBRATION_HIGH;
316 }
317 
CalibrateHigh()318 void Ui::CalibrateHigh() {
319   bool success = cv_scaler_->CalibrateHigh();
320   if (success) {
321     settings_->Save();
322     mode_ = UI_MODE_NORMAL;
323   } else {
324     mode_ = UI_MODE_PANIC;
325   }
326 }
327 
DoEvents()328 void Ui::DoEvents() {
329   while (queue_.available()) {
330     Event e = queue_.PullEvent();
331     if (e.control_type == CONTROL_SWITCH) {
332       if (e.data == 0) {
333         OnSwitchPressed(e);
334       } else {
335         OnSwitchReleased(e);
336       }
337     }
338   }
339   if (queue_.idle_time() > 800 && mode_ == UI_MODE_PANIC) {
340     mode_ = UI_MODE_NORMAL;
341   }
342   if (mode_ == UI_MODE_EASTER_EGG_INTRO || mode_ == UI_MODE_EASTER_EGG_OUTRO) {
343     if (queue_.idle_time() > 3000) {
344       mode_ = UI_MODE_NORMAL;
345       queue_.Touch();
346     }
347   } else if (queue_.idle_time() > 1000) {
348     queue_.Touch();
349   }
350 }
351 
HandleFactoryTestingRequest(uint8_t command)352 uint8_t Ui::HandleFactoryTestingRequest(uint8_t command) {
353   uint8_t argument = command & 0x1f;
354   command = command >> 5;
355   uint8_t reply = 0;
356   switch (command) {
357     case FACTORY_TESTING_READ_POT:
358     case FACTORY_TESTING_READ_CV:
359       reply = cv_scaler_->adc_value(argument);
360       break;
361 
362     case FACTORY_TESTING_READ_NORMALIZATION:
363       reply = cv_scaler_->normalization(argument);
364       break;
365 
366     case FACTORY_TESTING_READ_GATE:
367       reply = argument == 2
368           ? cv_scaler_->gate_value()
369           : switches_.pressed(argument);
370       break;
371 
372     case FACTORY_TESTING_SET_BYPASS:
373       part_->set_bypass(argument);
374       break;
375 
376     case FACTORY_TESTING_CALIBRATE:
377       {
378         switch (argument) {
379           case 0:
380             StartCalibration();
381             break;
382 
383           case 1:
384             CalibrateC1();
385             break;
386 
387           case 2:
388             CalibrateC3();
389             break;
390 
391           case 3:
392             StartNormalizationCalibration();
393             break;
394 
395           case 4:
396             CalibrateLow();
397             break;
398 
399           case 5:
400             CalibrateHigh();
401             queue_.Touch();
402             break;
403         }
404       }
405       break;
406   }
407   return reply;
408 }
409 
410 }  // namespace rings
411