1 // Copyright 2014 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 "clouds/ui.h"
30
31 #include "stmlib/system/system_clock.h"
32
33 #include "clouds/dsp/granular_processor.h"
34 #include "clouds/cv_scaler.h"
35 #include "clouds/meter.h"
36
37 namespace clouds {
38
39 const int32_t kLongPressDuration = 1000;
40 const int32_t kVeryLongPressDuration = 2500;
41
42 using namespace stmlib;
43
Init(Settings * settings,CvScaler * cv_scaler,GranularProcessor * processor,Meter * meter)44 void Ui::Init(
45 Settings* settings,
46 CvScaler* cv_scaler,
47 GranularProcessor* processor,
48 Meter* meter) {
49 settings_ = settings;
50 cv_scaler_ = cv_scaler;
51 leds_.Init();
52 switches_.Init();
53
54 processor_ = processor;
55 meter_ = meter;
56 mode_ = UI_MODE_SPLASH;
57 ignore_releases_ = 0;
58
59 const State& state = settings_->state();
60
61 // Sanitize saved settings.
62 cv_scaler_->set_blend_parameter(
63 static_cast<BlendParameter>(state.blend_parameter & 3));
64 processor_->set_quality(state.quality & 3);
65 processor_->set_playback_mode(
66 static_cast<PlaybackMode>(state.playback_mode % PLAYBACK_MODE_LAST));
67 for (int32_t i = 0; i < BLEND_PARAMETER_LAST; ++i) {
68 cv_scaler_->set_blend_value(
69 static_cast<BlendParameter>(i),
70 static_cast<float>(state.blend_value[i]) / 255.0f);
71 }
72 cv_scaler_->UnlockBlendKnob();
73
74 if (switches_.pressed_immediate(SWITCH_WRITE)) {
75 mode_ = UI_MODE_CALIBRATION_1;
76 ignore_releases_ = 1;
77 }
78 }
79
SaveState()80 void Ui::SaveState() {
81 State* state = settings_->mutable_state();
82 state->blend_parameter = cv_scaler_->blend_parameter();
83 state->quality = processor_->quality();
84 state->playback_mode = processor_->playback_mode();
85 for (int32_t i = 0; i < BLEND_PARAMETER_LAST; ++i) {
86 state->blend_value[i] = static_cast<uint8_t>(
87 cv_scaler_->blend_value(static_cast<BlendParameter>(i)) * 255.0f);
88 }
89 settings_->Save();
90 }
91
Poll()92 void Ui::Poll() {
93 system_clock.Tick();
94 switches_.Debounce();
95
96 for (uint8_t i = 0; i < kNumSwitches; ++i) {
97 if (switches_.just_pressed(i)) {
98 queue_.AddEvent(CONTROL_SWITCH, i, 0);
99 press_time_[i] = system_clock.milliseconds();
100 long_press_time_[i] = system_clock.milliseconds();
101 }
102 if (switches_.pressed(i) && press_time_[i] != 0) {
103 int32_t pressed_time = system_clock.milliseconds() - press_time_[i];
104 if (pressed_time > kLongPressDuration) {
105 queue_.AddEvent(CONTROL_SWITCH, i, pressed_time);
106 press_time_[i] = 0;
107 }
108 }
109 if (switches_.pressed(i) && long_press_time_[i] != 0) {
110 int32_t pressed_time = system_clock.milliseconds() - long_press_time_[i];
111 if (pressed_time > kVeryLongPressDuration) {
112 queue_.AddEvent(CONTROL_SWITCH, i, pressed_time);
113 long_press_time_[i] = 0;
114 }
115 }
116
117 if (switches_.released(i) && press_time_[i] != 0) {
118 queue_.AddEvent(
119 CONTROL_SWITCH,
120 i,
121 system_clock.milliseconds() - press_time_[i] + 1);
122 press_time_[i] = 0;
123 }
124 }
125 PaintLeds();
126 }
127
PaintLeds()128 void Ui::PaintLeds() {
129 leds_.Clear();
130 uint32_t clock = system_clock.milliseconds();
131 bool blink = (clock & 127) > 64;
132 bool flash = (clock & 511) < 16;
133 uint8_t fade = clock >> 1;
134 fade = fade <= 127 ? (fade << 1) : 255 - (fade << 1);
135 fade = static_cast<uint16_t>(fade) * fade >> 8;
136 switch (mode_) {
137 case UI_MODE_SPLASH:
138 {
139 uint8_t index = ((clock >> 8) + 1) & 3;
140 uint8_t fade = (clock >> 2);
141 fade = fade <= 127 ? (fade << 1) : 255 - (fade << 1);
142 leds_.set_intensity(3 - index, fade);
143 }
144 break;
145
146 case UI_MODE_VU_METER:
147 leds_.PaintBar(lut_db[meter_->peak() >> 7]);
148 break;
149
150 case UI_MODE_BLEND_METER:
151 for (int32_t i = 0; i < 4; ++i) {
152 leds_.set_intensity(
153 i,
154 cv_scaler_->blend_value(static_cast<BlendParameter>(i)) * 255.0f);
155 }
156 break;
157
158 case UI_MODE_QUALITY:
159 leds_.set_status(processor_->quality(), 255, 0);
160 break;
161
162 case UI_MODE_BLENDING:
163 leds_.set_status(cv_scaler_->blend_parameter(), 0, 255);
164 break;
165
166 case UI_MODE_PLAYBACK_MODE:
167 if (blink) {
168 for (int i=0; i<4; i++)
169 leds_.set_status(i, 0, 0);
170 } else if (processor_->playback_mode() < 4) {
171 leds_.set_status(processor_->playback_mode(),
172 128 + (fade >> 1),
173 255 - (fade >> 1));
174 } else {
175 for (int i=0; i<4; i++)
176 leds_.set_status(i, 128 + (fade >> 1), 255 - (fade >> 1));
177 leds_.set_status(processor_->playback_mode() & 3, 0, 0);
178 }
179
180 break;
181
182 case UI_MODE_LOAD:
183 leds_.set_status(load_save_location_, 0, blink ? 255 : 0);
184 break;
185
186 case UI_MODE_SAVE:
187 leds_.set_status(load_save_location_, blink ? 255 : 0, 0);
188 break;
189
190 case UI_MODE_SAVING:
191 leds_.set_status(load_save_location_, 255, 0);
192 break;
193
194 case UI_MODE_CALIBRATION_1:
195 leds_.set_status(0, blink ? 255 : 0, blink ? 255 : 0);
196 leds_.set_status(1, blink ? 255 : 0, blink ? 255 : 0);
197 leds_.set_status(2, 0, 0);
198 leds_.set_status(3, 0, 0);
199 break;
200
201 case UI_MODE_CALIBRATION_2:
202 leds_.set_status(0, blink ? 255 : 0, blink ? 255 : 0);
203 leds_.set_status(1, blink ? 255 : 0, blink ? 255 : 0);
204 leds_.set_status(2, blink ? 255 : 0, blink ? 255 : 0);
205 leds_.set_status(3, blink ? 255 : 0, blink ? 255 : 0);
206 break;
207
208 case UI_MODE_PANIC:
209 leds_.set_status(0, 255, 0);
210 leds_.set_status(1, 255, 0);
211 leds_.set_status(2, 255, 0);
212 leds_.set_status(3, 255, 0);
213 break;
214
215 default:
216 break;
217 }
218
219 bool freeze = processor_->frozen();
220 if (processor_->reversed()) {
221 freeze ^= flash;
222 }
223 leds_.set_freeze(freeze);
224 if (processor_->bypass()) {
225 leds_.PaintBar(lut_db[meter_->peak() >> 7]);
226 leds_.set_freeze(true);
227 }
228
229 leds_.Write();
230 }
231
FlushEvents()232 void Ui::FlushEvents() {
233 queue_.Flush();
234 }
235
OnSwitchPressed(const Event & e)236 void Ui::OnSwitchPressed(const Event& e) {
237 // double press -> feature switch mode
238 if ((e.control_id == SWITCH_MODE && switches_.pressed_immediate(SWITCH_WRITE)) ||
239 (e.control_id == SWITCH_WRITE && switches_.pressed_immediate(SWITCH_MODE))) {
240 mode_ = UI_MODE_PLAYBACK_MODE;
241 ignore_releases_ = 2;
242 }
243 }
244
CalibrateC1()245 void Ui::CalibrateC1() {
246 cv_scaler_->CalibrateC1();
247 cv_scaler_->CalibrateOffsets();
248 mode_ = UI_MODE_CALIBRATION_2;
249 }
250
CalibrateC3()251 void Ui::CalibrateC3() {
252 bool success = cv_scaler_->CalibrateC3();
253 if (success) {
254 settings_->Save();
255 mode_ = UI_MODE_VU_METER;
256 } else {
257 mode_ = UI_MODE_PANIC;
258 }
259 }
260
OnSecretHandshake()261 void Ui::OnSecretHandshake() {
262 mode_ = UI_MODE_PLAYBACK_MODE;
263 }
264
OnSwitchReleased(const Event & e)265 void Ui::OnSwitchReleased(const Event& e) {
266
267 // hack for double presses
268 if (ignore_releases_ > 0) {
269 ignore_releases_--;
270 return;
271 }
272
273 switch (e.control_id) {
274 case SWITCH_FREEZE:
275 if (e.data >= kVeryLongPressDuration) {
276 } else if (e.data >= kLongPressDuration) {
277 processor_->ToggleReverse();
278 } else {
279 processor_->ToggleFreeze();
280 }
281 break;
282
283 case SWITCH_MODE:
284 if (e.data >= kVeryLongPressDuration) {
285 mode_ = UI_MODE_PLAYBACK_MODE;
286 } else if (e.data >= kLongPressDuration) {
287 if (mode_ == UI_MODE_QUALITY) {
288 mode_ = UI_MODE_VU_METER;
289 } else {
290 mode_ = UI_MODE_QUALITY;
291 }
292 } else if (mode_ == UI_MODE_VU_METER || mode_ == UI_MODE_BLEND_METER) {
293 mode_ = UI_MODE_BLENDING;
294 } else if (mode_ == UI_MODE_BLENDING) {
295 uint8_t parameter = (cv_scaler_->blend_parameter() + 1) & 3;
296 cv_scaler_->set_blend_parameter(static_cast<BlendParameter>(parameter));
297 SaveState();
298 } else if (mode_ == UI_MODE_QUALITY) {
299 processor_->set_quality((processor_->quality() + 1) & 3);
300 SaveState();
301 } else if (mode_ == UI_MODE_PLAYBACK_MODE) {
302 uint8_t mode = processor_->playback_mode() == 0 ?
303 PLAYBACK_MODE_LAST-1 :
304 processor_->playback_mode() - 1;
305 processor_->set_playback_mode(static_cast<PlaybackMode>(mode));
306 SaveState();
307 } else if (mode_ == UI_MODE_SAVE || mode_ == UI_MODE_LOAD) {
308 load_save_location_ = (load_save_location_ + 1) & 3;
309 } else {
310 mode_ = UI_MODE_VU_METER;
311 }
312 break;
313
314 case SWITCH_WRITE:
315 if (mode_ == UI_MODE_CALIBRATION_1) {
316 CalibrateC1();
317 } else if (mode_ == UI_MODE_CALIBRATION_2) {
318 CalibrateC3();
319 } else if (mode_ == UI_MODE_SAVE) {
320 // Get pointers on data chunks to save.
321 PersistentBlock blocks[4];
322 size_t num_blocks = 0;
323
324 mode_ = UI_MODE_SAVING;
325 // Silence the processor during the long erase/write.
326 processor_->set_silence(true);
327 system_clock.Delay(5);
328 processor_->PreparePersistentData();
329 processor_->GetPersistentData(blocks, &num_blocks);
330 settings_->SaveSampleMemory(load_save_location_, blocks, num_blocks);
331 processor_->set_silence(false);
332 load_save_location_ = (load_save_location_ + 1) & 3;
333 mode_ = UI_MODE_VU_METER;
334 } else if (mode_ == UI_MODE_LOAD) {
335 processor_->LoadPersistentData(settings_->sample_flash_data(
336 load_save_location_));
337 load_save_location_ = (load_save_location_ + 1) & 3;
338 mode_ = UI_MODE_VU_METER;
339 } else if (mode_ == UI_MODE_PLAYBACK_MODE) {
340 uint8_t mode = (processor_->playback_mode() + 1) % PLAYBACK_MODE_LAST;
341 processor_->set_playback_mode(static_cast<PlaybackMode>(mode));
342 SaveState();
343 } else if (e.data >= kLongPressDuration) {
344 mode_ = UI_MODE_SAVE;
345 } else {
346 mode_ = UI_MODE_LOAD;
347 }
348 break;
349 }
350 }
351
DoEvents()352 void Ui::DoEvents() {
353 while (queue_.available()) {
354 Event e = queue_.PullEvent();
355 if (e.control_type == CONTROL_SWITCH) {
356 if (e.data == 0) {
357 OnSwitchPressed(e);
358 } else {
359 if (e.data >= kLongPressDuration &&
360 e.control_id == SWITCH_MODE &&
361 switches_.pressed(SWITCH_WRITE)) {
362 press_time_[SWITCH_WRITE] = 0;
363 OnSecretHandshake();
364 } else {
365 OnSwitchReleased(e);
366 }
367 }
368 }
369 }
370
371 if (queue_.idle_time() > 1000 && mode_ == UI_MODE_PANIC) {
372 queue_.Touch();
373 mode_ = UI_MODE_VU_METER;
374 }
375
376 if ((mode_ == UI_MODE_VU_METER || mode_ == UI_MODE_BLEND_METER ||
377 mode_ == UI_MODE_BLENDING) && \
378 cv_scaler_->blend_knob_touched()) {
379 queue_.Touch();
380 mode_ = UI_MODE_BLEND_METER;
381 }
382
383 if (queue_.idle_time() > 3000) {
384 queue_.Touch();
385 if (mode_ == UI_MODE_BLENDING || mode_ == UI_MODE_QUALITY ||
386 mode_ == UI_MODE_PLAYBACK_MODE || mode_ == UI_MODE_SAVE ||
387 mode_ == UI_MODE_LOAD || mode_ == UI_MODE_BLEND_METER ||
388 mode_ == UI_MODE_SPLASH) {
389 mode_ = UI_MODE_VU_METER;
390 }
391 }
392 }
393
HandleFactoryTestingRequest(uint8_t command)394 uint8_t Ui::HandleFactoryTestingRequest(uint8_t command) {
395 uint8_t argument = command & 0x1f;
396 command = command >> 5;
397 uint8_t reply = 0;
398 switch (command) {
399 case FACTORY_TESTING_READ_POT:
400 case FACTORY_TESTING_READ_CV:
401 reply = cv_scaler_->adc_value(argument);
402 break;
403
404 case FACTORY_TESTING_READ_GATE:
405 if (argument <= 2) {
406 return switches_.pressed(argument);
407 } else {
408 return cv_scaler_->gate(argument - 3);
409 }
410 break;
411
412 case FACTORY_TESTING_SET_BYPASS:
413 processor_->set_bypass(argument);
414 break;
415
416 case FACTORY_TESTING_CALIBRATE:
417 if (argument == 0) {
418 mode_ = UI_MODE_CALIBRATION_1;
419 } else if (argument == 1) {
420 CalibrateC1();
421 } else {
422 CalibrateC3();
423 cv_scaler_->set_blend_parameter(static_cast<BlendParameter>(0));
424 SaveState();
425 }
426 break;
427 }
428 return reply;
429 }
430
431 } // namespace clouds
432