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