1 // Copyright 2013 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 "streams/ui.h"
30 
31 #include <algorithm>
32 
33 #include "stmlib/system/storage.h"
34 #include "stmlib/system/system_clock.h"
35 
36 #include "streams/drivers/adc.h"
37 #include "streams/processor.h"
38 
39 namespace streams {
40 
41 const int32_t kLongPressDuration = 1000;
42 
43 using namespace std;
44 using namespace stmlib;
45 
46 Storage<0x801fc00, 4> ui_settings_storage;
47 
Init(Adc * adc,CvScaler * cv_scaler,Processor * processor)48 void Ui::Init(Adc* adc, CvScaler* cv_scaler, Processor* processor) {
49   leds_.Init();
50   switches_.Init();
51   adc_ = adc;
52   cv_scaler_ = cv_scaler;
53   processor_ = processor;
54 
55   fill(&pot_value_[0], &pot_value_[kNumPots], 0);
56   fill(&pot_threshold_[0], &pot_threshold_[kNumPots], 0);
57 
58   if (!ui_settings_storage.ParsimoniousLoad(&ui_settings_, &version_token_)) {
59     // Flash is not formatted. Initialize.
60     for (uint8_t i = 0; i < kNumChannels; ++i) {
61       ui_settings_.function[i] = PROCESSOR_FUNCTION_ENVELOPE;
62       ui_settings_.alternate[i] = false;
63     }
64     ui_settings_.monitor_mode = MONITOR_MODE_OUTPUT;
65     ui_settings_.linked = false;
66   }
67 
68   // Initialize from settings in flash.
69   monitor_mode_ = static_cast<MonitorMode>(ui_settings_.monitor_mode);
70   for (uint8_t i = 0; i < kNumChannels; ++i) {
71     meter_[i].Init();
72     processor_[i].set_alternate(ui_settings_.alternate[i]);
73     processor_[i].set_linked(ui_settings_.linked);
74     processor_[i].set_function(
75         static_cast<ProcessorFunction>(ui_settings_.function[i]));
76     display_mode_[i] = DISPLAY_MODE_MONITOR;
77   }
78 
79   secret_handshake_counter_ = 0;
80   factory_testing_ = switches_.pressed_immediate(SWITCH_MONITOR);
81 }
82 
SaveState()83 void Ui::SaveState() {
84   ui_settings_.monitor_mode = monitor_mode_;
85   ui_settings_.linked = processor_[0].linked();
86   ui_settings_.function[0] = processor_[0].function();
87   ui_settings_.function[1] = processor_[1].function();
88   ui_settings_.alternate[0] = processor_[0].alternate();
89   ui_settings_.alternate[1] = processor_[1].alternate();
90   ui_settings_storage.ParsimoniousSave(ui_settings_, &version_token_);
91 }
92 
PaintAdaptive(uint8_t channel,int32_t sample,int32_t gain)93 void Ui::PaintAdaptive(uint8_t channel, int32_t sample, int32_t gain) {
94   meter_[channel].Process(sample);
95   if (meter_[channel].cv()) {
96     sample = sample * lut_2164_gain[-gain >> 9] >> 15;
97     leds_.PaintCv(channel, sample * 5 >> 2);
98   } else {
99     leds_.PaintPositiveBar(channel, wav_db[meter_[channel].peak() >> 7] + gain);
100   }
101 }
102 
PaintMonitor(uint8_t channel)103 void Ui::PaintMonitor(uint8_t channel) {
104   switch (monitor_mode_) {
105     case MONITOR_MODE_EXCITE_IN:
106       PaintAdaptive(channel, cv_scaler_->excite_sample(channel), 0);
107       break;
108 
109     case MONITOR_MODE_AUDIO_IN:
110       PaintAdaptive(channel, cv_scaler_->audio_sample(channel), 0);
111       break;
112 
113     case MONITOR_MODE_VCA_CV:
114       leds_.PaintPositiveBar(channel, 32768 + cv_scaler_->gain_sample(channel));
115       break;
116 
117     case MONITOR_MODE_OUTPUT:
118       if (processor_[channel].function() == PROCESSOR_FUNCTION_COMPRESSOR) {
119         leds_.PaintNegativeBar(channel, processor_[channel].gain_reduction());
120       } else {
121         PaintAdaptive(
122             channel,
123             cv_scaler_->audio_sample(channel),
124             cv_scaler_->gain_sample(channel));
125       }
126       break;
127 
128     default:
129       break;
130   }
131 }
132 
PaintTestStatus()133 void Ui::PaintTestStatus() {
134   int32_t value = 0;
135   value += pot_value_[0] - 32768;
136   value += pot_value_[1] - 32768;
137   value += pot_value_[2] - 32768;
138   value += pot_value_[3] - 32768;
139   value -= cv_scaler_->excite_sample(0) > 0 ? 0 : cv_scaler_->excite_sample(0);
140   value -= cv_scaler_->excite_sample(1) > 0 ? 0 : cv_scaler_->excite_sample(1);
141   value -= cv_scaler_->gain_sample(0);
142   value -= cv_scaler_->gain_sample(1);
143   value -= cv_scaler_->audio_sample(0) - 12000;
144   value -= cv_scaler_->audio_sample(1) - 12000;
145   if (switches_.pressed(0)) value = -32767;
146   if (switches_.pressed(1)) value = 32767;
147   CONSTRAIN(value, -32767, 32767);
148   if (value < 12288 && value > -12288) value = 0;
149   uint8_t r = value >= 0 ? 0 : ((-1 - value) >> 7);
150   uint8_t g = value > 0 ? (value >> 7) : 0;
151   for (uint8_t i = 0; i < 8; ++i) {
152     leds_.set(i, r, g);
153   }
154 }
155 
PaintLeds()156 void Ui::PaintLeds() {
157   leds_.Clear();
158 
159   if (calibrating_) {
160     for (uint8_t i = 0; i < kNumChannels; ++i) {
161       uint8_t red, green;
162       if (show_offset_level_ & (1 << i)) {
163         int32_t gain_sample = cv_scaler_->raw_gain_sample(i);
164         bool nulled = gain_sample > 59000;
165         green = nulled ? 255 : 0;
166         red = nulled ? 0 : 255;
167       } else {
168         red = 0;
169         green = 255;
170       }
171       uint8_t pattern = i == 0 ? 255 : 9;
172       for (uint8_t j = 0; j < 4; ++j) {
173         bool on = (pattern & (1 << j)) != 0;
174         leds_.set(i * 4 + j, on ? red : 0, on ? green : 0);
175       }
176     }
177     return;
178   }
179 
180   if (factory_testing_) {
181     PaintTestStatus();
182     return;
183   }
184 
185   for (uint8_t i = 0; i < kNumChannels; ++i) {
186     uint8_t bank = i * 4;
187     switch (display_mode_[i]) {
188       case DISPLAY_MODE_FUNCTION:
189         {
190           bool alternate = processor_[i].alternate();
191           uint8_t intensity = 255;
192           if (processor_[i].linked()) {
193             uint8_t phase = system_clock.milliseconds() >> 1;
194             phase += i * 128;
195             phase = phase < 128 ? phase : (255 - phase);
196             intensity = (phase * 224 >> 7) + 32;
197             intensity = intensity * intensity >> 8;
198           }
199           uint8_t function = processor_[i].function();
200           if (function == PROCESSOR_FUNCTION_FILTER_CONTROLLER) {
201             for (uint8_t j = 0; j < 4; ++j) {
202               leds_.set(bank + j,
203                   alternate ? intensity : 0,
204                   alternate ? 0 : intensity);
205             }
206           } else if (function < PROCESSOR_FUNCTION_LORENZ_GENERATOR) {
207             leds_.set(
208                 bank + function,
209                 alternate ? intensity : 0,
210                 alternate ? 0 : intensity);
211           } else {
212             uint8_t index = (processor_[i].last_gain() >> 4) * 5 >> 4;
213             if (index > 3) index = 3;
214             int16_t color = processor_[i].last_frequency();
215             color = color - 128;
216             color *= 2;
217             if (color < 0) {
218               if (color < -127) color = -127;
219               leds_.set(bank + index, 255 + (color * 2), 255);
220             } else {
221               if (color > 127) color = 127;
222               leds_.set(bank + index, 255, 255 - (color * 2));
223             }
224           }
225         }
226         break;
227 
228       case DISPLAY_MODE_MONITOR_FUNCTION:
229         {
230           uint8_t position = static_cast<uint8_t>(monitor_mode_);
231           leds_.set(position * 2, 255, 0);
232           leds_.set(position * 2 + 1, 255, 0);
233         }
234         break;
235 
236       case DISPLAY_MODE_MONITOR:
237         PaintMonitor(i);
238         break;
239     }
240   }
241 }
242 
Poll()243 void Ui::Poll() {
244   // SysTick is at 4kHz to get a fast bargraph refresh.
245   ++divider_;
246   if ((divider_ & 3) == 0) {
247     system_clock.Tick();
248     switches_.Debounce();
249 
250     for (uint8_t i = 0; i < kNumSwitches; ++i) {
251       if (switches_.just_pressed(i)) {
252         queue_.AddEvent(CONTROL_SWITCH, i, 0);
253         press_time_[i] = system_clock.milliseconds();
254       }
255       if (switches_.pressed(i) && press_time_[i] != 0) {
256         int32_t pressed_time = system_clock.milliseconds() - press_time_[i];
257         if (pressed_time > kLongPressDuration) {
258           queue_.AddEvent(CONTROL_SWITCH, i, pressed_time);
259           press_time_[i] = 0;
260         }
261       }
262       if (switches_.released(i) && press_time_[i] != 0) {
263         queue_.AddEvent(
264             CONTROL_SWITCH,
265             i,
266             system_clock.milliseconds() - press_time_[i] + 1);
267         press_time_[i] = 0;
268       }
269     }
270 
271     adc_->ScanPots();
272     for (uint8_t i = 0; i < kNumPots; ++i) {
273       int32_t value = adc_->pot(i);
274       int32_t current_value = pot_value_[i];
275       if (value >= current_value + pot_threshold_[i] ||
276           value <= current_value - pot_threshold_[i] ||
277           !pot_threshold_[i]) {
278         Event e;
279         e.control_id = i;
280         e.data = value;
281         queue_.AddEvent(CONTROL_POT, i, e.data);
282         pot_value_[i] = value;
283         pot_threshold_[i] = 256;
284       }
285     }
286   }
287   PaintLeds();
288   leds_.Write();
289 }
290 
FlushEvents()291 void Ui::FlushEvents() {
292   queue_.Flush();
293 }
294 
Link(uint8_t index)295 void Ui::Link(uint8_t index) {
296   if (processor_[0].linked()) {
297     for (uint8_t i = 0; i < kNumChannels; ++i) {
298       if (i != index) {
299         display_mode_[i] = display_mode_[index];
300         processor_[i].set_function(processor_[index].function());
301         processor_[i].set_alternate(processor_[index].alternate());
302       }
303     }
304   }
305 }
306 
OnPotMoved(const Event & e)307 void Ui::OnPotMoved(const Event& e) {
308   if (calibrating_) {
309     if ((e.control_id & 1) == 0) {
310       int32_t min = kDefaultOffset >> 1;
311       int32_t max = 3 * min + 256;
312       int32_t value = min + ((max - min) * e.data >> 16);
313       cv_scaler_->set_dac_offset(e.control_id >> 1, value);
314       show_offset_level_ |= (1 << (e.control_id >> 1));
315     }
316   } else {
317     processor_[0].set_global(e.control_id, e.data);
318     processor_[1].set_global(e.control_id, e.data);
319     processor_[e.control_id >> 1].set_parameter(e.control_id & 1, e.data);
320   }
321 }
322 
OnSwitchPressed(const Event & e)323 void Ui::OnSwitchPressed(const Event& e) {
324   if (factory_testing_) {
325     if (e.control_id == SWITCH_MONITOR) {
326       ++secret_handshake_counter_;
327       if (secret_handshake_counter_ == 4) {
328         factory_testing_ = false;
329       }
330     }
331     return;
332   }
333   if (calibrating_) {
334     cv_scaler_->SaveCalibrationData();
335     calibrating_ = false;
336     show_offset_level_ = 0;
337     return;
338   }
339   // Double press!
340   if ((e.control_id == SWITCH_MODE_1 && press_time_[SWITCH_MODE_2]) ||
341       (e.control_id == SWITCH_MODE_2 && press_time_[SWITCH_MODE_1])) {
342     press_time_[SWITCH_MODE_1] = press_time_[SWITCH_MODE_2] = 0;
343     bool linked = !processor_[0].linked();
344     for (uint8_t i = 0; i < kNumChannels; ++i) {
345       display_mode_[i] = DISPLAY_MODE_FUNCTION;
346       processor_[i].set_linked(linked);
347     }
348     Link(1 - e.control_id);
349     return;
350   }
351 
352   switch (e.control_id) {
353     case SWITCH_MONITOR:
354       {
355         if (display_mode_[0] == DISPLAY_MODE_MONITOR &&
356             display_mode_[1] == DISPLAY_MODE_MONITOR) {
357           display_mode_[0] = display_mode_[1] = DISPLAY_MODE_MONITOR_FUNCTION;
358         } else if (display_mode_[0] == DISPLAY_MODE_MONITOR_FUNCTION &&
359                    display_mode_[1] == DISPLAY_MODE_MONITOR_FUNCTION) {
360           monitor_mode_ = static_cast<MonitorMode>(monitor_mode_ + 1);
361           if (monitor_mode_ == MONITOR_MODE_LAST) {
362             monitor_mode_ = static_cast<MonitorMode>(0);
363           }
364           SaveState();
365         } else {
366           display_mode_[0] = display_mode_[1] = DISPLAY_MODE_MONITOR;
367         }
368       }
369       break;
370 
371     default:
372       break;
373   }
374 }
375 
OnSwitchReleased(const Event & e)376 void Ui::OnSwitchReleased(const Event& e) {
377   if (factory_testing_) {
378     return;
379   }
380 
381   // Detect secret handshake for easter egg...
382   uint8_t secret_handshake_code = e.control_id;
383   secret_handshake_code |= e.data >= kLongPressDuration ? 2 : 0;
384   if ((secret_handshake_counter_ & 3) == secret_handshake_code) {
385     ++secret_handshake_counter_;
386     if (secret_handshake_counter_ == 16) {
387       for (uint8_t i = 0; i < kNumChannels; ++i) {
388         processor_[i].set_alternate(false);
389         processor_[i].set_function(PROCESSOR_FUNCTION_LORENZ_GENERATOR);
390       }
391       SaveState();
392       secret_handshake_counter_ = 0;
393       return;
394     }
395   } else {
396     secret_handshake_counter_ = 0;
397   }
398 
399   if (e.data >= kLongPressDuration) {
400     // Handle long presses.
401     switch (e.control_id) {
402       case SWITCH_MONITOR:
403         calibrating_ = cv_scaler_->can_calibrate();
404         if (calibrating_) {
405           cv_scaler_->CaptureAdcOffsets();
406           show_offset_level_ = 0;
407         }
408         break;
409 
410       case SWITCH_MODE_1:
411       case SWITCH_MODE_2:
412         {
413           processor_[e.control_id].set_alternate(
414               !processor_[e.control_id].alternate());
415           if (processor_[e.control_id].function() >
416               PROCESSOR_FUNCTION_COMPRESSOR) {
417             processor_[e.control_id].set_function(PROCESSOR_FUNCTION_ENVELOPE);
418           }
419           display_mode_[e.control_id] = DISPLAY_MODE_FUNCTION;
420           int32_t other = 1 - e.control_id;
421           if (display_mode_[other] == DISPLAY_MODE_MONITOR_FUNCTION) {
422             display_mode_[other] = DISPLAY_MODE_MONITOR;
423           }
424           Link(e.control_id);
425           SaveState();
426         }
427         break;
428     }
429   } else {
430     switch (e.control_id) {
431       case SWITCH_MODE_1:
432       case SWITCH_MODE_2:
433         {
434           if (display_mode_[e.control_id] == DISPLAY_MODE_FUNCTION) {
435             ProcessorFunction index = processor_[e.control_id].function();
436             index = static_cast<ProcessorFunction>(index + 1);
437             ProcessorFunction limit = processor_[e.control_id].alternate()
438                 ? PROCESSOR_FUNCTION_FILTER_CONTROLLER
439                 : PROCESSOR_FUNCTION_COMPRESSOR;
440             if (index > limit) {
441               index = static_cast<ProcessorFunction>(0);
442             }
443             processor_[e.control_id].set_function(index);
444             SaveState();
445           } else {
446             display_mode_[e.control_id] = DISPLAY_MODE_FUNCTION;
447             int32_t other = 1 - e.control_id;
448             if (display_mode_[other] == DISPLAY_MODE_MONITOR_FUNCTION) {
449               display_mode_[other] = DISPLAY_MODE_MONITOR;
450             }
451           }
452           Link(e.control_id);
453         }
454         break;
455 
456       default:
457         break;
458     }
459   }
460 }
461 
DoEvents()462 void Ui::DoEvents() {
463   bool refresh = false;
464   while (queue_.available()) {
465     Event e = queue_.PullEvent();
466     if (e.control_type == CONTROL_SWITCH) {
467       if (e.data == 0) {
468         OnSwitchPressed(e);
469       } else {
470         OnSwitchReleased(e);
471       }
472     } else if (e.control_type == CONTROL_POT) {
473       OnPotMoved(e);
474     }
475     refresh = true;
476   }
477   if (queue_.idle_time() > 1000) {
478     queue_.Touch();
479     if (display_mode_[0] == DISPLAY_MODE_MONITOR_FUNCTION &&
480         display_mode_[1] == DISPLAY_MODE_MONITOR_FUNCTION) {
481        display_mode_[0] = display_mode_[1] = DISPLAY_MODE_MONITOR;
482     }
483     refresh = true;
484   }
485 
486   // Recompute processor parameters if necessary.
487   if (refresh) {
488     for (uint8_t i = 0; i < kNumChannels; ++i) {
489       processor_[i].Configure();
490     }
491   }
492 }
493 
494 }  // namespace streams
495