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 "elements/ui.h"
30
31 #include <algorithm>
32
33 #include "stmlib/system/system_clock.h"
34
35 #include "elements/cv_scaler.h"
36 #include "elements/dsp/part.h"
37
38 namespace elements {
39
40 using namespace std;
41 using namespace stmlib;
42
Init(Part * part,CvScaler * cv_scaler)43 void Ui::Init(Part* part, CvScaler* cv_scaler) {
44 leds_.Init();
45 switch_.Init();
46 mode_ = UI_MODE_NORMAL; // UI_MODE_DISPLAY_MODEL;
47 part_ = part;
48 cv_scaler_ = cv_scaler;
49 part_->set_easter_egg(cv_scaler_->boot_in_easter_egg_mode());
50 part_->set_resonator_model(ResonatorModel(cv_scaler_->resonator_model()));
51 }
52
Poll()53 void Ui::Poll() {
54 // 1kHz.
55 system_clock.Tick();
56 switch_.Debounce();
57 if (switch_.just_pressed()) {
58 queue_.AddEvent(CONTROL_SWITCH, 0, 0);
59 press_time_ = system_clock.milliseconds();
60 }
61
62 if (switch_.pressed() && \
63 press_time_ &&
64 (system_clock.milliseconds() - press_time_) >= 3000) {
65 if (cv_scaler_->ready_for_calibration()) {
66 queue_.AddEvent(CONTROL_SWITCH, 1, 0);
67 press_time_ = 0;
68 } else if (cv_scaler_->resonator_high()) {
69 if (cv_scaler_->exciter_low()) {
70 queue_.AddEvent(CONTROL_SWITCH, 2, 0);
71 } else {
72 queue_.AddEvent(CONTROL_SWITCH, 3, 0);
73 }
74 press_time_ = 0;
75 }
76 }
77
78 if (switch_.released() && press_time_) {
79 queue_.AddEvent(
80 CONTROL_SWITCH,
81 0,
82 system_clock.milliseconds() - press_time_ + 1);
83 }
84
85 bool blink = (system_clock.milliseconds() & 127) > 64;
86 switch (mode_) {
87 case UI_MODE_NORMAL:
88 leds_.set_gate(part_->gate());
89 leds_.set_exciter(
90 lut_db_led_brightness[int32_t(part_->exciter_level() * 512.0f)]);
91 leds_.set_resonator(
92 lut_db_led_brightness[int32_t(part_->resonator_level() * 512.0f)]);
93 break;
94
95 case UI_MODE_CALIBRATION_1:
96 leds_.set_gate(!blink);
97 leds_.set_exciter(blink ? 255 : 0);
98 leds_.set_resonator(0);
99 break;
100
101 case UI_MODE_CALIBRATION_2:
102 leds_.set_gate(!blink);
103 leds_.set_exciter(0);
104 leds_.set_resonator(blink ? 255 : 0);
105 break;
106
107 case UI_MODE_PANIC:
108 leds_.set_gate(blink);
109 leds_.set_exciter(blink ? 255 : 0);
110 leds_.set_resonator(blink ? 0 : 255);
111 break;
112
113 case UI_MODE_DISPLAY_MODEL:
114 {
115 bool blink = (system_clock.milliseconds() & 255) > 128;
116 uint8_t count = ((system_clock.milliseconds()) >> 8) & 3;
117 bool pulse = (count <= part_->resonator_model()) && blink;
118 leds_.set_gate(pulse);
119 leds_.set_exciter(pulse ? 255 : 0);
120 leds_.set_resonator(pulse ? 255 : 0);
121 }
122 break;
123 }
124
125 if (part_->bypass()) {
126 leds_.set_gate(true);
127 leds_.set_exciter(255);
128 leds_.set_resonator(255);
129 }
130
131 leds_.Write();
132 }
133
FlushEvents()134 void Ui::FlushEvents() {
135 queue_.Flush();
136 }
137
OnSwitchPressed(const Event & e)138 void Ui::OnSwitchPressed(const Event& e) {
139 switch (e.control_id) {
140 case 0:
141 if (mode_ == UI_MODE_CALIBRATION_1) {
142 CalibrateC1();
143 } else if (mode_ == UI_MODE_CALIBRATION_2) {
144 CalibrateC3();
145 } else {
146 gate_ = true;
147 }
148 break;
149
150 case 1:
151 mode_ = UI_MODE_CALIBRATION_1;
152 break;
153
154 case 2:
155 part_->set_easter_egg(!part_->easter_egg());
156 cv_scaler_->set_boot_in_easter_egg_mode(part_->easter_egg());
157 cv_scaler_->SaveCalibration();
158 gate_ = false;
159 break;
160
161 case 3:
162 part_->set_resonator_model(
163 ResonatorModel((part_->resonator_model() + 1) % 3));
164 cv_scaler_->set_resonator_model(part_->resonator_model());
165 cv_scaler_->SaveCalibration();
166 gate_ = false;
167 mode_ = UI_MODE_DISPLAY_MODEL;
168 break;
169
170 default:
171 break;
172 }
173 }
174
OnSwitchReleased(const Event & e)175 void Ui::OnSwitchReleased(const Event& e) {
176 gate_ = false;
177 }
178
CalibrateC1()179 void Ui::CalibrateC1() {
180 cv_scaler_->CalibrateC1();
181 cv_scaler_->CalibrateOffsets();
182 mode_ = UI_MODE_CALIBRATION_2;
183 }
184
CalibrateC3()185 void Ui::CalibrateC3() {
186 bool success = cv_scaler_->CalibrateC3();
187 if (success) {
188 cv_scaler_->SaveCalibration();
189 mode_ = UI_MODE_NORMAL;
190 } else {
191 mode_ = UI_MODE_PANIC;
192 }
193 }
194
DoEvents()195 void Ui::DoEvents() {
196 while (queue_.available()) {
197 Event e = queue_.PullEvent();
198 if (e.control_type == CONTROL_SWITCH) {
199 if (e.data == 0) {
200 OnSwitchPressed(e);
201 } else {
202 OnSwitchReleased(e);
203 }
204 }
205 }
206 if (mode_ == UI_MODE_DISPLAY_MODEL) {
207 if (queue_.idle_time() > 4000) {
208 mode_ = UI_MODE_NORMAL;
209 queue_.Touch();
210 }
211 } else {
212 if (queue_.idle_time() > 800 && mode_ == UI_MODE_PANIC) {
213 mode_ = UI_MODE_NORMAL;
214 }
215 if (queue_.idle_time() > 1000) {
216 queue_.Touch();
217 }
218 }
219 }
220
HandleFactoryTestingRequest(uint8_t command)221 uint8_t Ui::HandleFactoryTestingRequest(uint8_t command) {
222 uint8_t argument = command & 0x1f;
223 command = command >> 5;
224 uint8_t reply = 0;
225 switch (command) {
226 case FACTORY_TESTING_READ_POT:
227 reply = cv_scaler_->pot_value(argument);
228 break;
229
230 case FACTORY_TESTING_READ_CV:
231 reply = cv_scaler_->cv_value(argument);
232 break;
233
234 case FACTORY_TESTING_READ_GATE:
235 if (argument == 0x00) {
236 return gate_;
237 } else {
238 return cv_scaler_->gate();
239 }
240 break;
241
242 case FACTORY_TESTING_SET_BYPASS:
243 part_->set_bypass(argument);
244 break;
245
246 case FACTORY_TESTING_CALIBRATE:
247 if (argument == 0) {
248 mode_ = UI_MODE_CALIBRATION_1;
249 } else if (argument == 1) {
250 CalibrateC1();
251 } else {
252 CalibrateC3();
253
254 }
255 break;
256 }
257 return reply;
258 }
259
260 } // namespace elements
261