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