You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

411 lines
11KB

  1. // Copyright 2015 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. #include "rings/ui.h"
  29. #include <algorithm>
  30. #include "stmlib/system/system_clock.h"
  31. #include "rings/cv_scaler.h"
  32. #include "rings/dsp/part.h"
  33. #include "rings/dsp/string_synth_part.h"
  34. namespace rings {
  35. const int32_t kLongPressDuration = 3000;
  36. using namespace std;
  37. using namespace stmlib;
  38. void Ui::Init(
  39. Settings* settings,
  40. CvScaler* cv_scaler,
  41. Part* part,
  42. StringSynthPart* string_synth) {
  43. leds_.Init();
  44. switches_.Init();
  45. settings_ = settings;
  46. cv_scaler_ = cv_scaler;
  47. part_ = part;
  48. string_synth_ = string_synth;
  49. part_->set_polyphony(settings_->state().polyphony);
  50. part_->set_model(static_cast<ResonatorModel>(settings_->state().model));
  51. string_synth_->set_polyphony(settings_->state().polyphony);
  52. string_synth_->set_fx(static_cast<FxType>(settings_->state().model));
  53. mode_ = settings_->state().easter_egg
  54. ? UI_MODE_EASTER_EGG_INTRO
  55. : UI_MODE_NORMAL;
  56. }
  57. void Ui::SaveState() {
  58. settings_->mutable_state()->polyphony = part_->polyphony();
  59. settings_->mutable_state()->model = part_->model();
  60. settings_->Save();
  61. }
  62. void Ui::AnimateEasterEggLeds() {
  63. mode_ = settings_->state().easter_egg
  64. ? UI_MODE_EASTER_EGG_INTRO
  65. : UI_MODE_EASTER_EGG_OUTRO;
  66. }
  67. void Ui::Poll() {
  68. // 1kHz.
  69. system_clock.Tick();
  70. switches_.Debounce();
  71. for (uint8_t i = 0; i < kNumSwitches; ++i) {
  72. if (switches_.just_pressed(i)) {
  73. queue_.AddEvent(CONTROL_SWITCH, i, 0);
  74. press_time_[i] = system_clock.milliseconds();
  75. }
  76. if (switches_.pressed(i) && press_time_[i] != 0) {
  77. int32_t pressed_time = system_clock.milliseconds() - press_time_[i];
  78. if (pressed_time > kLongPressDuration) {
  79. queue_.AddEvent(CONTROL_SWITCH, i, pressed_time);
  80. press_time_[i] = 0;
  81. }
  82. }
  83. if (switches_.released(i) && press_time_[i] != 0) {
  84. queue_.AddEvent(
  85. CONTROL_SWITCH,
  86. i,
  87. system_clock.milliseconds() - press_time_[i] + 1);
  88. press_time_[i] = 0;
  89. }
  90. }
  91. bool blink = (system_clock.milliseconds() & 127) > 64;
  92. bool slow_blink = (system_clock.milliseconds() & 255) > 128;
  93. switch (mode_) {
  94. case UI_MODE_NORMAL:
  95. {
  96. uint8_t pwm_counter = system_clock.milliseconds() & 15;
  97. uint8_t triangle = (system_clock.milliseconds() >> 5) & 31;
  98. triangle = triangle < 16 ? triangle : 31 - triangle;
  99. leds_.set(0, part_->polyphony() >= 2, part_->polyphony() <= 2);
  100. leds_.set(1, part_->model() >= 1, part_->model() <= 1);
  101. // Fancy modes!
  102. if (part_->polyphony() == 3) {
  103. leds_.set(0, true, pwm_counter < triangle);
  104. }
  105. if (part_->model() >= 3) {
  106. bool led_1 = part_->model() >= 4 && pwm_counter < triangle;
  107. bool led_2 = part_->model() <= 4 && pwm_counter < triangle;
  108. leds_.set(1, led_1, led_2);
  109. }
  110. ++strumming_flag_interval_;
  111. if (strumming_flag_counter_) {
  112. --strumming_flag_counter_;
  113. leds_.set(0, false, false);
  114. }
  115. }
  116. break;
  117. case UI_MODE_CALIBRATION_C1:
  118. leds_.set(0, blink, blink);
  119. leds_.set(1, false, false);
  120. break;
  121. case UI_MODE_CALIBRATION_C3:
  122. leds_.set(0, false, false);
  123. leds_.set(1, blink, blink);
  124. break;
  125. case UI_MODE_CALIBRATION_LOW:
  126. leds_.set(0, slow_blink, 0);
  127. leds_.set(1, slow_blink, 0);
  128. break;
  129. case UI_MODE_CALIBRATION_HIGH:
  130. leds_.set(0, false, slow_blink);
  131. leds_.set(1, false, slow_blink);
  132. break;
  133. case UI_MODE_EASTER_EGG_INTRO:
  134. {
  135. uint8_t pwm_counter = system_clock.milliseconds() & 15;
  136. uint8_t triangle_1 = (system_clock.milliseconds() / 7) & 31;
  137. uint8_t triangle_2 = (system_clock.milliseconds() / 17) & 31;
  138. triangle_1 = triangle_1 < 16 ? triangle_1 : 31 - triangle_1;
  139. triangle_2 = triangle_2 < 16 ? triangle_2 : 31 - triangle_2;
  140. leds_.set(
  141. 0,
  142. triangle_1 > pwm_counter,
  143. triangle_2 > pwm_counter);
  144. leds_.set(
  145. 1,
  146. triangle_2 > pwm_counter,
  147. triangle_1 > pwm_counter);
  148. }
  149. break;
  150. case UI_MODE_EASTER_EGG_OUTRO:
  151. {
  152. uint8_t pwm_counter = 7;
  153. uint8_t triangle_1 = (system_clock.milliseconds() / 9) & 31;
  154. uint8_t triangle_2 = (system_clock.milliseconds() / 13) & 31;
  155. triangle_1 = triangle_1 < 16 ? triangle_1 : 31 - triangle_1;
  156. triangle_2 = triangle_2 < 16 ? triangle_2 : 31 - triangle_2;
  157. leds_.set(0, triangle_1 < pwm_counter, triangle_1 > pwm_counter);
  158. leds_.set(1, triangle_2 > pwm_counter, triangle_2 < pwm_counter);
  159. }
  160. break;
  161. case UI_MODE_PANIC:
  162. leds_.set(0, blink, false);
  163. leds_.set(1, blink, false);
  164. break;
  165. }
  166. leds_.Write();
  167. }
  168. void Ui::FlushEvents() {
  169. queue_.Flush();
  170. }
  171. void Ui::OnSwitchPressed(const Event& e) {
  172. }
  173. void Ui::OnSwitchReleased(const Event& e) {
  174. // Check if the other switch is still pressed.
  175. if (switches_.pressed(1 - e.control_id)) {
  176. if (mode_ == UI_MODE_CALIBRATION_C1) {
  177. StartNormalizationCalibration();
  178. } else {
  179. StartCalibration();
  180. }
  181. press_time_[0] = press_time_[1] = 0;
  182. return;
  183. }
  184. switch (e.control_id) {
  185. case 0:
  186. if (e.data >= kLongPressDuration) {
  187. if (cv_scaler_->easter_egg()) {
  188. settings_->ToggleEasterEgg();
  189. AnimateEasterEggLeds();
  190. } else {
  191. part_->set_polyphony(3);
  192. string_synth_->set_polyphony(3);
  193. }
  194. SaveState();
  195. } else {
  196. switch (mode_) {
  197. case UI_MODE_CALIBRATION_C1:
  198. CalibrateC1();
  199. break;
  200. case UI_MODE_CALIBRATION_C3:
  201. CalibrateC3();
  202. break;
  203. case UI_MODE_CALIBRATION_LOW:
  204. CalibrateLow();
  205. break;
  206. case UI_MODE_CALIBRATION_HIGH:
  207. CalibrateHigh();
  208. break;
  209. default:
  210. {
  211. int32_t polyphony = part_->polyphony();
  212. if (polyphony == 3) {
  213. polyphony = 2;
  214. }
  215. polyphony <<= 1;
  216. if (polyphony > 4) {
  217. polyphony = 1;
  218. }
  219. part_->set_polyphony(polyphony);
  220. string_synth_->set_polyphony(polyphony);
  221. SaveState();
  222. }
  223. break;
  224. }
  225. }
  226. break;
  227. case 1:
  228. if (e.data >= kLongPressDuration) {
  229. if (cv_scaler_->easter_egg()) {
  230. settings_->ToggleEasterEgg();
  231. AnimateEasterEggLeds();
  232. } else {
  233. int32_t model = part_->model();
  234. if (model >= 3) {
  235. model -= 3;
  236. } else {
  237. model += 3;
  238. }
  239. part_->set_model(static_cast<ResonatorModel>(model));
  240. string_synth_->set_fx(static_cast<FxType>(model));
  241. }
  242. } else {
  243. int32_t model = part_->model();
  244. if (model >= 3) {
  245. model -= 3;
  246. } else {
  247. model = (model + 1) % 3;
  248. }
  249. part_->set_model(static_cast<ResonatorModel>(model));
  250. string_synth_->set_fx(static_cast<FxType>(model));
  251. }
  252. SaveState();
  253. break;
  254. default:
  255. break;
  256. }
  257. }
  258. void Ui::StartCalibration() {
  259. mode_ = UI_MODE_CALIBRATION_C1;
  260. }
  261. void Ui::CalibrateC1() {
  262. cv_scaler_->CalibrateC1();
  263. cv_scaler_->CalibrateOffsets();
  264. mode_ = UI_MODE_CALIBRATION_C3;
  265. }
  266. void Ui::CalibrateC3() {
  267. bool success = cv_scaler_->CalibrateC3();
  268. if (success) {
  269. settings_->Save();
  270. mode_ = UI_MODE_NORMAL;
  271. } else {
  272. mode_ = UI_MODE_PANIC;
  273. }
  274. }
  275. void Ui::StartNormalizationCalibration() {
  276. cv_scaler_->StartNormalizationCalibration();
  277. mode_ = UI_MODE_CALIBRATION_LOW;
  278. }
  279. void Ui::CalibrateLow() {
  280. cv_scaler_->CalibrateLow();
  281. mode_ = UI_MODE_CALIBRATION_HIGH;
  282. }
  283. void Ui::CalibrateHigh() {
  284. bool success = cv_scaler_->CalibrateHigh();
  285. if (success) {
  286. settings_->Save();
  287. mode_ = UI_MODE_NORMAL;
  288. } else {
  289. mode_ = UI_MODE_PANIC;
  290. }
  291. }
  292. void Ui::DoEvents() {
  293. while (queue_.available()) {
  294. Event e = queue_.PullEvent();
  295. if (e.control_type == CONTROL_SWITCH) {
  296. if (e.data == 0) {
  297. OnSwitchPressed(e);
  298. } else {
  299. OnSwitchReleased(e);
  300. }
  301. }
  302. }
  303. if (queue_.idle_time() > 800 && mode_ == UI_MODE_PANIC) {
  304. mode_ = UI_MODE_NORMAL;
  305. }
  306. if (mode_ == UI_MODE_EASTER_EGG_INTRO || mode_ == UI_MODE_EASTER_EGG_OUTRO) {
  307. if (queue_.idle_time() > 3000) {
  308. mode_ = UI_MODE_NORMAL;
  309. queue_.Touch();
  310. }
  311. } else if (queue_.idle_time() > 1000) {
  312. queue_.Touch();
  313. }
  314. }
  315. uint8_t Ui::HandleFactoryTestingRequest(uint8_t command) {
  316. uint8_t argument = command & 0x1f;
  317. command = command >> 5;
  318. uint8_t reply = 0;
  319. switch (command) {
  320. case FACTORY_TESTING_READ_POT:
  321. case FACTORY_TESTING_READ_CV:
  322. reply = cv_scaler_->adc_value(argument);
  323. break;
  324. case FACTORY_TESTING_READ_NORMALIZATION:
  325. reply = cv_scaler_->normalization(argument);
  326. break;
  327. case FACTORY_TESTING_READ_GATE:
  328. reply = argument == 2
  329. ? cv_scaler_->gate_value()
  330. : switches_.pressed(argument);
  331. break;
  332. case FACTORY_TESTING_SET_BYPASS:
  333. part_->set_bypass(argument);
  334. break;
  335. case FACTORY_TESTING_CALIBRATE:
  336. {
  337. switch (argument) {
  338. case 0:
  339. StartCalibration();
  340. break;
  341. case 1:
  342. CalibrateC1();
  343. break;
  344. case 2:
  345. CalibrateC3();
  346. break;
  347. case 3:
  348. StartNormalizationCalibration();
  349. break;
  350. case 4:
  351. CalibrateLow();
  352. break;
  353. case 5:
  354. CalibrateHigh();
  355. queue_.Touch();
  356. break;
  357. }
  358. }
  359. break;
  360. }
  361. return reply;
  362. }
  363. } // namespace rings