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