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