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