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.

543 lines
16KB

  1. // Copyright 2016 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. // UI and CV processing ("controller" and "view")
  28. #include "plaits/ui.h"
  29. #include <algorithm>
  30. #include "stmlib/dsp/dsp.h"
  31. #include "stmlib/system/system_clock.h"
  32. namespace plaits {
  33. using namespace std;
  34. using namespace stmlib;
  35. static const int32_t kLongPressTime = 2000;
  36. #define ENABLE_LFO_MODE
  37. void Ui::Init(Patch* patch, Modulations* modulations, Settings* settings) {
  38. patch_ = patch;
  39. modulations_ = modulations;
  40. settings_ = settings;
  41. cv_adc_.Init();
  42. pots_adc_.Init();
  43. leds_.Init();
  44. switches_.Init();
  45. ui_task_ = 0;
  46. mode_ = UI_MODE_NORMAL;
  47. LoadState();
  48. if (switches_.pressed_immediate(SWITCH_ROW_2)) {
  49. State* state = settings_->mutable_state();
  50. if (state->color_blind == 1) {
  51. state->color_blind = 0;
  52. } else {
  53. state->color_blind = 1;
  54. }
  55. settings_->SaveState();
  56. }
  57. // Bind pots to parameters.
  58. pots_[POTS_ADC_CHANNEL_FREQ_POT].Init(
  59. &transposition_, NULL, 2.0f, -1.0f);
  60. pots_[POTS_ADC_CHANNEL_HARMONICS_POT].Init(
  61. &patch->harmonics, &octave_, 1.0f, 0.0f);
  62. pots_[POTS_ADC_CHANNEL_TIMBRE_POT].Init(
  63. &patch->timbre, &patch->lpg_colour, 1.0f, 0.0f);
  64. pots_[POTS_ADC_CHANNEL_MORPH_POT].Init(
  65. &patch->morph, &patch->decay, 1.0f, 0.0f);
  66. pots_[POTS_ADC_CHANNEL_TIMBRE_ATTENUVERTER].Init(
  67. &patch->timbre_modulation_amount, NULL, 2.0f, -1.0f);
  68. pots_[POTS_ADC_CHANNEL_FM_ATTENUVERTER].Init(
  69. &patch->frequency_modulation_amount, NULL, 2.0f, -1.0f);
  70. pots_[POTS_ADC_CHANNEL_MORPH_ATTENUVERTER].Init(
  71. &patch->morph_modulation_amount, NULL, 2.0f, -1.0f);
  72. // Keep track of the agreement between the random sequence sent to the
  73. // switch and the value read by the ADC.
  74. normalization_detection_count_ = 0;
  75. normalization_probe_state_ = 0;
  76. normalization_probe_.Init();
  77. fill(
  78. &normalization_detection_mismatches_[0],
  79. &normalization_detection_mismatches_[5],
  80. 0);
  81. pwm_counter_ = 0;
  82. fill(&press_time_[0], &press_time_[SWITCH_LAST], 0);
  83. fill(&ignore_release_[0], &ignore_release_[SWITCH_LAST], false);
  84. active_engine_ = 0;
  85. cv_c1_ = 0.0f;
  86. pitch_lp_ = 0.0f;
  87. pitch_lp_calibration_ = 0.0f;
  88. }
  89. void Ui::LoadState() {
  90. const State& state = settings_->state();
  91. patch_->engine = state.engine;
  92. patch_->lpg_colour = static_cast<float>(state.lpg_colour) / 256.0f;
  93. patch_->decay = static_cast<float>(state.decay) / 256.0f;
  94. octave_ = static_cast<float>(state.octave) / 256.0f;
  95. }
  96. void Ui::SaveState() {
  97. State* state = settings_->mutable_state();
  98. state->engine = patch_->engine;
  99. state->lpg_colour = static_cast<uint8_t>(patch_->lpg_colour * 256.0f);
  100. state->decay = static_cast<uint8_t>(patch_->decay * 256.0f);
  101. state->octave = static_cast<uint8_t>(octave_ * 256.0f);
  102. settings_->SaveState();
  103. }
  104. void Ui::UpdateLEDs() {
  105. leds_.Clear();
  106. ++pwm_counter_;
  107. int pwm_counter = pwm_counter_ & 15;
  108. int triangle = (pwm_counter_ >> 4) & 31;
  109. triangle = triangle < 16 ? triangle : 31 - triangle;
  110. switch (mode_) {
  111. case UI_MODE_NORMAL:
  112. {
  113. LedColor red = settings_->state().color_blind == 1
  114. ? ((pwm_counter & 7) ? LED_COLOR_OFF : LED_COLOR_YELLOW)
  115. : LED_COLOR_RED;
  116. LedColor green = settings_->state().color_blind == 1
  117. ? LED_COLOR_YELLOW
  118. : LED_COLOR_GREEN;
  119. leds_.set(
  120. active_engine_ & 7,
  121. active_engine_ & 8 ? red : green);
  122. if (pwm_counter < triangle) {
  123. leds_.mask(
  124. patch_->engine & 7,
  125. patch_->engine & 8 ? red : green);
  126. }
  127. }
  128. break;
  129. case UI_MODE_DISPLAY_ALTERNATE_PARAMETERS:
  130. {
  131. for (int parameter = 0; parameter < 2; ++parameter) {
  132. float value = parameter == 0
  133. ? patch_->lpg_colour
  134. : patch_->decay;
  135. value -= 0.001f;
  136. for (int i = 0; i < 4; ++i) {
  137. leds_.set(
  138. parameter * 4 + 3 - i,
  139. value * 64.0f > pwm_counter ? LED_COLOR_YELLOW : LED_COLOR_OFF);
  140. value -= 0.25f;
  141. }
  142. }
  143. }
  144. break;
  145. case UI_MODE_DISPLAY_OCTAVE:
  146. {
  147. #ifdef ENABLE_LFO_MODE
  148. int octave = static_cast<float>(octave_ * 10.0f);
  149. for (int i = 0; i < 8; ++i) {
  150. LedColor color = LED_COLOR_OFF;
  151. if (octave == 0) {
  152. color = i == (triangle >> 1) ? LED_COLOR_OFF : LED_COLOR_YELLOW;
  153. } else if (octave == 9) {
  154. color = LED_COLOR_YELLOW;
  155. } else {
  156. color = (octave - 1) == i ? LED_COLOR_YELLOW : LED_COLOR_OFF;
  157. }
  158. leds_.set(7 - i, color);
  159. }
  160. #else
  161. int octave = static_cast<float>(octave_ * 9.0f);
  162. for (int i = 0; i < 8; ++i) {
  163. leds_.set(
  164. 7 - i,
  165. octave == i || (octave == 8) ? LED_COLOR_YELLOW : LED_COLOR_OFF);
  166. }
  167. #endif // ENABLE_LFO_MODE
  168. }
  169. break;
  170. case UI_MODE_CALIBRATION_C1:
  171. if (pwm_counter < triangle) {
  172. leds_.set(0, LED_COLOR_GREEN);
  173. }
  174. break;
  175. case UI_MODE_CALIBRATION_C3:
  176. if (pwm_counter < triangle) {
  177. leds_.set(0, LED_COLOR_YELLOW);
  178. }
  179. break;
  180. case UI_MODE_ERROR:
  181. if (pwm_counter < triangle) {
  182. for (int i = 0; i < kNumLEDs; ++i) {
  183. leds_.set(i, LED_COLOR_RED);
  184. }
  185. }
  186. break;
  187. case UI_MODE_TEST:
  188. int color = (pwm_counter_ >> 10) % 3;
  189. for (int i = 0; i < kNumLEDs; ++i) {
  190. leds_.set(
  191. i, pwm_counter > ((triangle + (i * 2)) & 15)
  192. ? (color == 0
  193. ? LED_COLOR_GREEN
  194. : (color == 1 ? LED_COLOR_YELLOW : LED_COLOR_RED))
  195. : LED_COLOR_OFF);
  196. }
  197. break;
  198. }
  199. leds_.Write();
  200. }
  201. void Ui::ReadSwitches() {
  202. switches_.Debounce();
  203. switch (mode_) {
  204. case UI_MODE_NORMAL:
  205. {
  206. for (int i = 0; i < SWITCH_LAST; ++i) {
  207. if (switches_.just_pressed(Switch(i))) {
  208. press_time_[i] = 0;
  209. ignore_release_[i] = false;
  210. }
  211. if (switches_.pressed(Switch(i))) {
  212. ++press_time_[i];
  213. } else {
  214. press_time_[i] = 0;
  215. }
  216. }
  217. if (switches_.just_pressed(Switch(0))) {
  218. pots_[POTS_ADC_CHANNEL_TIMBRE_POT].Lock();
  219. pots_[POTS_ADC_CHANNEL_MORPH_POT].Lock();
  220. }
  221. if (switches_.just_pressed(Switch(1))) {
  222. pots_[POTS_ADC_CHANNEL_HARMONICS_POT].Lock();
  223. }
  224. if (pots_[POTS_ADC_CHANNEL_MORPH_POT].editing_hidden_parameter() ||
  225. pots_[POTS_ADC_CHANNEL_TIMBRE_POT].editing_hidden_parameter()) {
  226. mode_ = UI_MODE_DISPLAY_ALTERNATE_PARAMETERS;
  227. }
  228. if (pots_[POTS_ADC_CHANNEL_HARMONICS_POT].editing_hidden_parameter()) {
  229. mode_ = UI_MODE_DISPLAY_OCTAVE;
  230. }
  231. // Long, double press: enter calibration mode.
  232. if (press_time_[0] >= kLongPressTime &&
  233. press_time_[1] >= kLongPressTime) {
  234. press_time_[0] = press_time_[1] = 0;
  235. RealignPots();
  236. StartCalibration();
  237. }
  238. // Long press or actually editing any hidden parameter: display value
  239. // of hidden parameters.
  240. if (press_time_[0] >= kLongPressTime && !press_time_[1]) {
  241. press_time_[0] = press_time_[1] = 0;
  242. mode_ = UI_MODE_DISPLAY_ALTERNATE_PARAMETERS;
  243. }
  244. if (press_time_[1] >= kLongPressTime && !press_time_[0]) {
  245. press_time_[0] = press_time_[1] = 0;
  246. mode_ = UI_MODE_DISPLAY_OCTAVE;
  247. }
  248. if (switches_.released(Switch(0)) && !ignore_release_[0]) {
  249. RealignPots();
  250. if (patch_->engine >= 8) {
  251. patch_->engine = patch_->engine & 7;
  252. } else {
  253. patch_->engine = (patch_->engine + 1) % 8;
  254. }
  255. SaveState();
  256. }
  257. if (switches_.released(Switch(1)) && !ignore_release_[1]) {
  258. RealignPots();
  259. if (patch_->engine < 8) {
  260. patch_->engine = (patch_->engine & 7) + 8;
  261. } else {
  262. patch_->engine = 8 + ((patch_->engine + 1) % 8);
  263. }
  264. SaveState();
  265. }
  266. }
  267. break;
  268. case UI_MODE_DISPLAY_ALTERNATE_PARAMETERS:
  269. case UI_MODE_DISPLAY_OCTAVE:
  270. for (int i = 0; i < SWITCH_LAST; ++i) {
  271. if (switches_.released(Switch(i))) {
  272. pots_[POTS_ADC_CHANNEL_TIMBRE_POT].Unlock();
  273. pots_[POTS_ADC_CHANNEL_MORPH_POT].Unlock();
  274. pots_[POTS_ADC_CHANNEL_HARMONICS_POT].Unlock();
  275. press_time_[i] = 0;
  276. mode_ = UI_MODE_NORMAL;
  277. }
  278. }
  279. break;
  280. case UI_MODE_CALIBRATION_C1:
  281. for (int i = 0; i < SWITCH_LAST; ++i) {
  282. if (switches_.just_pressed(Switch(i))) {
  283. press_time_[i] = 0;
  284. ignore_release_[i] = true;
  285. CalibrateC1();
  286. break;
  287. }
  288. }
  289. break;
  290. case UI_MODE_CALIBRATION_C3:
  291. for (int i = 0; i < SWITCH_LAST; ++i) {
  292. if (switches_.just_pressed(Switch(i))) {
  293. press_time_[i] = 0;
  294. ignore_release_[i] = true;
  295. CalibrateC3();
  296. break;
  297. }
  298. }
  299. break;
  300. case UI_MODE_TEST:
  301. case UI_MODE_ERROR:
  302. for (int i = 0; i < SWITCH_LAST; ++i) {
  303. if (switches_.just_pressed(Switch(i))) {
  304. press_time_[i] = 0;
  305. ignore_release_[i] = true;
  306. mode_ = UI_MODE_NORMAL;
  307. }
  308. }
  309. break;
  310. }
  311. }
  312. void Ui::ProcessPotsHiddenParameters() {
  313. for (int i = 0; i < POTS_ADC_CHANNEL_LAST; ++i) {
  314. pots_[i].ProcessUIRate();
  315. }
  316. }
  317. /* static */
  318. const CvAdcChannel Ui::normalized_channels_[] = {
  319. CV_ADC_CHANNEL_FM,
  320. CV_ADC_CHANNEL_TIMBRE,
  321. CV_ADC_CHANNEL_MORPH,
  322. CV_ADC_CHANNEL_TRIGGER,
  323. CV_ADC_CHANNEL_LEVEL,
  324. };
  325. void Ui::DetectNormalization() {
  326. bool expected_value = normalization_probe_state_ >> 31;
  327. for (int i = 0; i < kNumNormalizedChannels; ++i) {
  328. CvAdcChannel channel = normalized_channels_[i];
  329. bool read_value = cv_adc_.value(channel) < \
  330. settings_->calibration_data(channel).normalization_detection_threshold;
  331. if (expected_value != read_value) {
  332. ++normalization_detection_mismatches_[i];
  333. }
  334. }
  335. ++normalization_detection_count_;
  336. if (normalization_detection_count_ == kProbeSequenceDuration) {
  337. normalization_detection_count_ = 0;
  338. bool* destination = &modulations_->frequency_patched;
  339. for (int i = 0; i < kNumNormalizedChannels; ++i) {
  340. destination[i] = normalization_detection_mismatches_[i] >= 2;
  341. normalization_detection_mismatches_[i] = 0;
  342. }
  343. }
  344. normalization_probe_state_ = 1103515245 * normalization_probe_state_ + 12345;
  345. normalization_probe_.Write(normalization_probe_state_ >> 31);
  346. }
  347. void Ui::Poll() {
  348. for (int i = 0; i < POTS_ADC_CHANNEL_LAST; ++i) {
  349. pots_[i].ProcessControlRate(pots_adc_.float_value(PotsAdcChannel(i)));
  350. }
  351. float* destination = &modulations_->engine;
  352. for (int i = 0; i < CV_ADC_CHANNEL_LAST; ++i) {
  353. destination[i] = settings_->calibration_data(i).Transform(
  354. cv_adc_.float_value(CvAdcChannel(i)));
  355. }
  356. ONE_POLE(pitch_lp_, modulations_->note, 0.7f);
  357. ONE_POLE(
  358. pitch_lp_calibration_, cv_adc_.float_value(CV_ADC_CHANNEL_V_OCT), 0.1f);
  359. modulations_->note = pitch_lp_;
  360. ui_task_ = (ui_task_ + 1) % 4;
  361. switch (ui_task_) {
  362. case 0:
  363. UpdateLEDs();
  364. break;
  365. case 1:
  366. ReadSwitches();
  367. break;
  368. case 2:
  369. ProcessPotsHiddenParameters();
  370. break;
  371. case 3:
  372. DetectNormalization();
  373. break;
  374. }
  375. cv_adc_.Convert();
  376. pots_adc_.Convert();
  377. #ifdef ENABLE_LFO_MODE
  378. int octave = static_cast<int>(octave_ * 10.0f);
  379. if (octave == 0) {
  380. patch_->note = -48.37f + transposition_ * 60.0f;
  381. } else if (octave == 9) {
  382. patch_->note = 60.0f + transposition_ * 48.0f;
  383. } else {
  384. const float fine = transposition_ * 7.0f;
  385. patch_->note = fine + static_cast<float>(octave) * 12.0f;
  386. }
  387. #else
  388. int octave = static_cast<int>(octave_ * 9.0f);
  389. if (octave < 8) {
  390. const float fine = transposition_ * 7.0f;
  391. patch_->note = fine + static_cast<float>(octave) * 12.0f + 12.0f;
  392. } else {
  393. patch_->note = 60.0f + transposition_ * 48.0f;
  394. }
  395. #endif // ENABLE_LFO_MODE
  396. }
  397. void Ui::StartCalibration() {
  398. mode_ = UI_MODE_CALIBRATION_C1;
  399. normalization_probe_.Disable();
  400. }
  401. void Ui::CalibrateC1() {
  402. // Acquire offsets for all channels.
  403. for (int i = 0; i < CV_ADC_CHANNEL_LAST; ++i) {
  404. if (i != CV_ADC_CHANNEL_V_OCT) {
  405. ChannelCalibrationData* c = settings_->mutable_calibration_data(i);
  406. c->offset = -cv_adc_.float_value(CvAdcChannel(i)) * c->scale;
  407. }
  408. }
  409. cv_c1_ = pitch_lp_calibration_;
  410. mode_ = UI_MODE_CALIBRATION_C3;
  411. }
  412. void Ui::CalibrateC3() {
  413. // (-33/100.0*1 + -33/140.0 * -10.0) / 3.3 * 2.0 - 1 = 0.228
  414. float c1 = cv_c1_;
  415. // (-33/100.0*1 + -33/140.0 * -10.0) / 3.3 * 2.0 - 1 = -0.171
  416. float c3 = pitch_lp_calibration_;
  417. float delta = c3 - c1;
  418. if (delta > -0.6f && delta < -0.2f) {
  419. ChannelCalibrationData* c = settings_->mutable_calibration_data(
  420. CV_ADC_CHANNEL_V_OCT);
  421. c->scale = 24.0f / delta;
  422. c->offset = 12.0f - c->scale * c1;
  423. settings_->SavePersistentData();
  424. mode_ = UI_MODE_NORMAL;
  425. } else {
  426. mode_ = UI_MODE_ERROR;
  427. }
  428. normalization_probe_.Init();
  429. }
  430. uint8_t Ui::HandleFactoryTestingRequest(uint8_t command) {
  431. uint8_t argument = command & 0x1f;
  432. command = command >> 5;
  433. uint8_t reply = 0;
  434. switch (command) {
  435. case FACTORY_TESTING_READ_POT:
  436. reply = pots_adc_.value(PotsAdcChannel(argument)) >> 8;
  437. break;
  438. case FACTORY_TESTING_READ_CV:
  439. reply = (cv_adc_.value(CvAdcChannel(argument)) + 32768) >> 8;
  440. break;
  441. case FACTORY_TESTING_READ_NORMALIZATION:
  442. reply = (&modulations_->frequency_patched)[argument] ? 0 : 255;
  443. break;
  444. case FACTORY_TESTING_READ_GATE:
  445. reply = switches_.pressed(Switch(argument));
  446. break;
  447. case FACTORY_TESTING_GENERATE_TEST_SIGNAL:
  448. if (argument) {
  449. mode_ = UI_MODE_TEST;
  450. } else {
  451. mode_ = UI_MODE_NORMAL;
  452. }
  453. break;
  454. case FACTORY_TESTING_CALIBRATE:
  455. {
  456. switch (argument) {
  457. case 0:
  458. patch_->engine = 0;
  459. StartCalibration();
  460. break;
  461. case 1:
  462. CalibrateC1();
  463. break;
  464. case 2:
  465. CalibrateC3();
  466. SaveState();
  467. break;
  468. }
  469. }
  470. break;
  471. }
  472. return reply;
  473. }
  474. } // namespace plaits