Advertisement
NoSloppy

Untitled

Dec 9th, 2023
40
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 54.69 KB | None | 0 0
  1. #ifndef PROPS_PROP_BASE_H
  2. #define PROPS_PROP_BASE_H
  3.  
  4. #ifndef PROP_INHERIT_PREFIX
  5. #define PROP_INHERIT_PREFIX
  6. #endif
  7.  
  8. #if !defined(DYNAMIC_CLASH_THRESHOLD) && defined(SAVE_CLASH_THRESHOLD)
  9. #undef SAVE_CLASH_THRESHOLD
  10. #endif
  11.  
  12. #if !defined(DYNAMIC_BLADE_DIMMING) && defined(SAVE_BLADE_DIMMING)
  13. #undef SAVE_BLADE_DIMMING
  14. #endif
  15.  
  16. #if !defined(ENABLE_AUDIO) && defined(SAVE_VOLUME)
  17. #undef SAVE_VOLUME
  18. #endif
  19.  
  20. #ifndef AUDIO_CLASH_SUPPRESSION_LEVEL
  21. // Account for Audio Volume in Clash Detection (range 1 ~ 50)
  22. #define AUDIO_CLASH_SUPPRESSION_LEVEL 10
  23. #endif
  24.  
  25. class SaveGlobalStateFile : public ConfigFile {
  26. public:
  27. void iterateVariables(VariableOP *op) override {
  28. #ifdef SAVE_CLASH_THRESHOLD
  29. CONFIG_VARIABLE2(clash_threshold, CLASH_THRESHOLD_G);
  30. #endif
  31. #ifdef SAVE_VOLUME
  32. CONFIG_VARIABLE2(volume, -1);
  33. #endif
  34. #ifdef SAVE_BLADE_DIMMING
  35. CONFIG_VARIABLE2(dimming, 16384);
  36. #endif
  37. }
  38. #ifdef SAVE_CLASH_THRESHOLD
  39. float clash_threshold;
  40. #endif
  41. #ifdef SAVE_VOLUME
  42. int volume;
  43. #endif
  44. #ifdef SAVE_BLADE_DIMMING
  45. int dimming;
  46. #endif
  47. };
  48.  
  49. class SavePresetStateFile : public ConfigFile {
  50. public:
  51. void iterateVariables(VariableOP *op) override {
  52. CONFIG_VARIABLE2(preset, 0);
  53. #ifdef DYNAMIC_BLADE_LENGTH
  54. #define BLADE_LEN_CONFIG_VARIABLE(N) CONFIG_VARIABLE2(blade##N##len, -1);
  55. ONCEPERBLADE(BLADE_LEN_CONFIG_VARIABLE);
  56. #endif
  57. }
  58. int preset;
  59. #ifdef DYNAMIC_BLADE_LENGTH
  60. #define BLADE_LEN_VARIABLE(N) int blade##N##len;
  61. ONCEPERBLADE(BLADE_LEN_VARIABLE);
  62. #endif
  63. };
  64.  
  65. #ifdef ENABLE_AUDIO
  66. struct SoundToPlay {
  67. const char* filename_;
  68. Effect* effect_;
  69. int selection_;
  70.  
  71. SoundToPlay() :filename_(nullptr), effect_(nullptr) {}
  72. explicit SoundToPlay(const char* file) : filename_(file){ }
  73. SoundToPlay(Effect* effect, int selection = -1) : filename_(nullptr), effect_(effect), selection_(selection) {}
  74. bool Play(BufferedWavPlayer* player) {
  75. if (filename_) return player->PlayInCurrentDir(filename_);
  76. effect_->Select(selection_);
  77. player->PlayOnce(effect_);
  78. return true;
  79. }
  80. bool isSet() {
  81. return filename_ != nullptr || effect_ != nullptr;
  82. }
  83. };
  84.  
  85. template<int QueueLength>
  86. class SoundQueue {
  87. public:
  88. bool Play(SoundToPlay p) {
  89. if (sounds_ < QueueLength) {
  90. queue_[sounds_++] = p;
  91. return true;
  92. }
  93. return false;
  94. }
  95. bool Play(const char* p) {
  96. return Play(SoundToPlay(p));
  97. }
  98. // Called from Loop()
  99. void PollSoundQueue(RefPtr<BufferedWavPlayer>& player) {
  100. if (sounds_ && (!player || !player->isPlaying())) {
  101. if (!player) {
  102. player = GetFreeWavPlayer();
  103. if (!player) return;
  104. player->set_volume_now(1.0f);
  105. }
  106. queue_[0].Play(player.get());
  107. sounds_--;
  108. for (int i = 0; i < sounds_; i++) queue_[i] = queue_[i+1];
  109. }
  110. }
  111. private:
  112. int sounds_;
  113. SoundToPlay queue_[QueueLength];
  114. };
  115. #endif
  116.  
  117. #include "../sound/sound_library.h" // BC method using sound_library for consistency of UI
  118. extern SoundLibrary sound_library_;
  119.  
  120. // Base class for props.
  121. class PropBase : CommandParser, Looper, protected SaberBase {
  122. public:
  123. PropBase() : CommandParser() {}
  124. BladeStyle* current_style() {
  125. #if NUM_BLADES == 0
  126. return nullptr;
  127. #else
  128. if (!current_config->blade1) return nullptr;
  129. return current_config->blade1->current_style();
  130. #endif
  131. }
  132. const char* current_preset_name() {
  133. return current_preset_.name.get();
  134. }
  135.  
  136. bool NeedsPower() {
  137. if (SaberBase::IsOn()) return true;
  138. if (current_style() && current_style()->NoOnOff())
  139. return true;
  140. return false;
  141. }
  142.  
  143. int32_t muted_volume_ = 0;
  144. bool SetMute(bool muted) {
  145. #ifdef ENABLE_AUDIO
  146. if (muted) {
  147. if (dynamic_mixer.get_volume()) {
  148. muted_volume_ = dynamic_mixer.get_volume();
  149. dynamic_mixer.set_volume(0);
  150. return true;
  151. }
  152. } else {
  153. if (muted_volume_) {
  154. dynamic_mixer.set_volume(muted_volume_);
  155. muted_volume_ = 0;
  156. return true;
  157. }
  158. }
  159. #endif
  160. return false;
  161. }
  162.  
  163. bool unmute_on_deactivation_ = false;
  164. uint32_t activated_ = 0;
  165. uint32_t last_clash_ = 0;
  166. uint32_t clash_timeout_ = 100;
  167.  
  168. bool clash_pending_ = false;
  169. bool pending_clash_is_stab_ = false;
  170. float pending_clash_strength_ = 0.0;
  171.  
  172. bool on_pending_ = false;
  173.  
  174. virtual bool IsOn() {
  175. return SaberBase::IsOn() || on_pending_;
  176. }
  177.  
  178. virtual void On() {
  179. #ifdef ENABLE_AUDIO
  180. if (!CommonIgnition()) return;
  181. SaberBase::DoPreOn();
  182. on_pending_ = true;
  183. // Hybrid font will call SaberBase::TurnOn() for us
  184. #else
  185. // No sound means no preon.
  186. FastOn();
  187. #endif
  188. }
  189.  
  190. void FastOn() {
  191. if (!CommonIgnition()) return;
  192. SaberBase::TurnOn();
  193. SaberBase::DoEffect(EFFECT_FAST_ON, 0);
  194. }
  195.  
  196. void SB_On() override {
  197. on_pending_ = false;
  198. }
  199.  
  200. virtual void Off(OffType off_type = OFF_NORMAL) {
  201. if (on_pending_) {
  202. // Or is it better to wait until we turn on, and then turn off?
  203. on_pending_ = false;
  204. SaberBase::TurnOff(SaberBase::OFF_CANCEL_PREON);
  205. return;
  206. }
  207. if (!SaberBase::IsOn()) return;
  208. if (SaberBase::Lockup()) {
  209. SaberBase::DoEndLockup();
  210. SaberBase::SetLockup(SaberBase::LOCKUP_NONE);
  211. }
  212. #ifndef DISABLE_COLOR_CHANGE
  213. if (SaberBase::GetColorChangeMode() != SaberBase::COLOR_CHANGE_MODE_NONE) {
  214. ToggleColorChangeMode();
  215. }
  216. #endif
  217. SaberBase::TurnOff(off_type);
  218. if (unmute_on_deactivation_) {
  219. unmute_on_deactivation_ = false;
  220. #ifdef ENABLE_AUDIO
  221. // We may also need to stop any thing else that generates noise..
  222. for (size_t i = 0; i < NELEM(wav_players); i++) {
  223. wav_players[i].Stop();
  224. }
  225. #endif
  226. SetMute(false);
  227. }
  228. }
  229.  
  230. #ifdef DYNAMIC_CLASH_THRESHOLD
  231. float clash_threshold_;
  232. float GetCurrentClashThreshold() { return clash_threshold_; }
  233. void SetClashThreshold(float clash_threshold) { clash_threshold_ = clash_threshold; }
  234. #undef CLASH_THRESHOLD_G
  235. #define CLASH_THRESHOLD_G clash_threshold_
  236. #else
  237. float GetCurrentClashThreshold() { return CLASH_THRESHOLD_G; }
  238. #endif
  239.  
  240. void IgnoreClash(size_t ms) {
  241. if (clash_pending_) return;
  242. uint32_t now = millis();
  243. uint32_t time_since_last_clash = now - last_clash_;
  244. if (time_since_last_clash < clash_timeout_) {
  245. ms = std::max<size_t>(ms, clash_timeout_ - time_since_last_clash);
  246. }
  247. last_clash_ = now;
  248. clash_timeout_ = ms;
  249. }
  250.  
  251. virtual void Clash2(bool stab, float strength) {
  252. SaberBase::SetClashStrength(strength);
  253. if (Event(BUTTON_NONE, stab ? EVENT_STAB : EVENT_CLASH)) {
  254. IgnoreClash(400);
  255. } else {
  256. IgnoreClash(100);
  257. // Saber must be on and not in lockup mode for stab/clash.
  258. if (SaberBase::IsOn() && !SaberBase::Lockup()) {
  259. if (stab) {
  260. SaberBase::DoStab();
  261. } else {
  262. SaberBase::DoClash();
  263. }
  264. }
  265. }
  266. }
  267.  
  268. virtual void Clash(bool stab, float strength) {
  269. // TODO: Pick clash randomly and/or based on strength of clash.
  270. uint32_t t = millis();
  271. if (t - last_clash_ < clash_timeout_) {
  272. if (clash_pending_) {
  273. pending_clash_strength_ = std::max<float>(pending_clash_strength_, strength);
  274. } else {
  275. SaberBase::UpdateClashStrength(strength);
  276. }
  277. last_clash_ = t; // Vibration cancellation
  278. return;
  279. }
  280. if (current_modifiers & ~MODE_ON) {
  281. // Some button is pressed, that means that we need to delay the clash a little
  282. // to see if was caused by a button *release*.
  283. last_clash_ = millis();
  284. clash_timeout_ = 3;
  285. clash_pending_ = true;
  286. pending_clash_is_stab_ = stab;
  287. pending_clash_strength_ = strength;
  288. return;
  289. }
  290. Clash2(stab, strength);
  291. }
  292.  
  293. virtual bool chdir(const char* dir) {
  294. if (strlen(dir) > 1 && dir[strlen(dir)-1] == '/') {
  295. STDOUT.println("Directory must not end with slash.");
  296. return false;
  297. }
  298. #ifdef ENABLE_AUDIO
  299. smooth_swing_v2.Deactivate();
  300. looped_swing_wrapper.Deactivate();
  301. hybrid_font.Deactivate();
  302.  
  303. // Stop all sound!
  304. // TODO: Move scanning to wav-playing interrupt level so we can
  305. // interleave things without worry about memory corruption.
  306. for (size_t i = 0; i < NELEM(wav_players); i++) {
  307. wav_players[i].Stop();
  308. }
  309. #endif
  310.  
  311. char *b = current_directory;
  312. for (const char *a = dir; *a; a++) {
  313. // Skip trailing slash
  314. if (*a == '/' && (a[1] == 0 || a[1] == ';'))
  315. continue;
  316. if (*a == ';') {
  317. *(b++) = 0;
  318. continue;
  319. }
  320. *(b++) = *a;
  321. }
  322. // Two zeroes at end!
  323. *(b++) = 0;
  324. *(b++) = 0;
  325.  
  326. Effect::ScanCurrentDirectory();
  327. #ifdef ENABLE_AUDIO
  328. SaberBase* font = NULL;
  329. hybrid_font.Activate();
  330. font = &hybrid_font;
  331. if (font) {
  332. smooth_swing_config.ReadInCurrentDir("smoothsw.ini");
  333. if (SFX_lswing) {
  334. smooth_swing_cfx_config.ReadInCurrentDir("font_config.txt");
  335. // map CFX values to Proffie (sourced from font_config.txt in font folder)
  336. smooth_swing_config.SwingSensitivity = smooth_swing_cfx_config.smooth_sens;
  337. smooth_swing_config.MaximumHumDucking = smooth_swing_cfx_config.smooth_dampen;
  338. smooth_swing_config.SwingSharpness = smooth_swing_cfx_config.smooth_sharp;
  339. smooth_swing_config.SwingStrengthThreshold = smooth_swing_cfx_config.smooth_gate;
  340. smooth_swing_config.Transition1Degrees = smooth_swing_cfx_config.smooth_width1;
  341. smooth_swing_config.Transition2Degrees = smooth_swing_cfx_config.smooth_width2;
  342. smooth_swing_config.MaxSwingVolume = smooth_swing_cfx_config.smooth_gain * 3 / 100;
  343. smooth_swing_config.AccentSwingSpeedThreshold = smooth_swing_cfx_config.hswing;
  344. smooth_swing_config.Version = 2;
  345. } else if (!SFX_swingl) {
  346. smooth_swing_config.Version = 0;
  347. }
  348. switch (smooth_swing_config.Version) {
  349. case 1:
  350. looped_swing_wrapper.Activate(font);
  351. break;
  352. case 2:
  353. smooth_swing_v2.Activate(font);
  354. break;
  355. }
  356. }
  357. // EnableBooster();
  358. #endif
  359. return false;
  360. }
  361.  
  362. void SaveVolumeIfNeeded() {
  363. if (0
  364. #ifdef SAVE_VOLUME
  365. || dynamic_mixer.get_volume() != saved_global_state.volume
  366. #endif
  367. #ifdef SAVE_BLADE_DIMMING
  368. || SaberBase::GetCurrentDimming() != saved_global_state.dimming
  369. #endif
  370. #ifdef SAVE_CLASH_THRESHOLD
  371. || GetCurrentClashThreshold() != saved_global_state.clash_threshold
  372. #endif
  373. ) {
  374. SaveGlobalState();
  375. }
  376. }
  377.  
  378. void SaveColorChangeIfNeeded() {
  379. #ifdef SAVE_COLOR_CHANGE
  380. if (current_preset_.variation != SaberBase::GetCurrentVariation()) {
  381. current_preset_.variation = SaberBase::GetCurrentVariation();
  382. current_preset_.Save();
  383. }
  384. #endif
  385. }
  386.  
  387. void PollSaveColorChange() {
  388. #ifdef ENABLE_AUDIO
  389. if (AmplifierIsActive()) return; // Do it later
  390. #endif
  391. SaveColorChangeIfNeeded();
  392. SaveVolumeIfNeeded();
  393. }
  394.  
  395. bool BladeOff() {
  396. #ifdef IDLE_OFF_TIME
  397. last_on_time_ = millis();
  398. #endif
  399. bool on = IsOn();
  400. if (on) Off();
  401. return on;
  402. }
  403.  
  404. void FreeBladeStyles() {
  405. #define UNSET_BLADE_STYLE(N) \
  406. delete current_config->blade##N->UnSetStyle();
  407. ONCEPERBLADE(UNSET_BLADE_STYLE)
  408. }
  409.  
  410. void AllocateBladeStyles() {
  411. #ifdef DYNAMIC_BLADE_LENGTH
  412. savestate_.ReadINIFromSaveDir("curstate");
  413. #define WRAP_BLADE_SHORTERNER(N) \
  414. if (savestate_.blade##N##len != -1 && savestate_.blade##N##len != current_config->blade##N->num_leds()) { \
  415. tmp = new BladeShortenerWrapper(savestate_.blade##N##len, tmp); \
  416. }
  417. #else
  418. #define WRAP_BLADE_SHORTERNER(N)
  419. #endif
  420. #define SET_BLADE_STYLE(N) do { \
  421. BladeStyle* tmp = style_parser.Parse(current_preset_.GetStyle(N)); \
  422. WRAP_BLADE_SHORTERNER(N) \
  423. current_config->blade##N->SetStyle(tmp); \
  424. } while (0);
  425.  
  426. ONCEPERBLADE(SET_BLADE_STYLE)
  427.  
  428. #ifdef SAVE_COLOR_CHANGE
  429. SaberBase::SetVariation(current_preset_.variation);
  430. #else
  431. SaberBase::SetVariation(0);
  432. #endif
  433. }
  434.  
  435. // Select preset (font/style)
  436. virtual void SetPreset(int preset_num, bool announce) {
  437. PVLOG_DEBUG << "SetPreset(" << preset_num << ")\n";
  438. TRACE(PROP, "start");
  439. bool on = BladeOff();
  440. SaveColorChangeIfNeeded();
  441. // First free all styles, then allocate new ones to avoid memory
  442. // fragmentation.
  443. FreeBladeStyles();
  444. current_preset_.SetPreset(preset_num);
  445.  
  446. AllocateBladeStyles();
  447. chdir(current_preset_.font.get());
  448. if (on) On();
  449. if (announce) {
  450. PVLOG_STATUS << "Current Preset: " << current_preset_name() << "\n";
  451. SaberBase::DoNewFont();
  452. }
  453. TRACE(PROP, "end");
  454. }
  455.  
  456. // Update Blade Style (no On/Off for use in Edit Mode)
  457. void UpdateStyle() {
  458. TRACE(PROP, "start");
  459. SaveColorChangeIfNeeded();
  460. // First free all styles, then allocate new ones to avoid memory
  461. // fragmentation.
  462. FreeBladeStyles();
  463. current_preset_.SetPreset(current_preset_.preset_num);
  464. AllocateBladeStyles();
  465. TRACE(PROP, "end");
  466. }
  467.  
  468. // Set/Update Font & Style, skips Preon effect using FastOn (for use in Edit Mode and "fast" preset changes)
  469. void SetPresetFast(int preset_num) {
  470. PVLOG_DEBUG << "SetPresetFast(" << preset_num << ")\n";
  471. TRACE(PROP, "start");
  472. bool on = BladeOff();
  473. SaveColorChangeIfNeeded();
  474. // First free all styles, then allocate new ones to avoid memory
  475. // fragmentation.
  476. FreeBladeStyles();
  477. current_preset_.SetPreset(preset_num);
  478. AllocateBladeStyles();
  479. chdir(current_preset_.font.get());
  480. if (on) FastOn();
  481. TRACE(PROP, "end");
  482. }
  483.  
  484. // Update Preon IntArg in Edit Mode
  485. void UpdatePreon() {
  486. TRACE(PROP, "start");
  487. bool on = BladeOff();
  488. SaveColorChangeIfNeeded();
  489. // First free all styles, then allocate new ones to avoid memory
  490. // fragmentation.
  491. FreeBladeStyles();
  492. current_preset_.SetPreset(current_preset_.preset_num);
  493. AllocateBladeStyles();
  494. chdir(current_preset_.font.get());
  495. if (on) On();
  496. TRACE(PROP, "end");
  497. }
  498.  
  499. // Go to the next Preset.
  500. virtual void next_preset() {
  501. #ifdef SAVE_PRESET
  502. SaveState(current_preset_.preset_num + 1);
  503. #endif
  504. SetPreset(current_preset_.preset_num + 1, true);
  505. }
  506.  
  507. // Go to the next Preset skipping NewFont and Preon effects using FastOn.
  508. void next_preset_fast() {
  509. #ifdef SAVE_PRESET
  510. SaveState(current_preset_.preset_num + 1);
  511. #endif
  512. SetPresetFast(current_preset_.preset_num + 1);
  513. }
  514.  
  515. // Go to the previous Preset.
  516. virtual void previous_preset() {
  517. #ifdef SAVE_PRESET
  518. SaveState(current_preset_.preset_num - 1);
  519. #endif
  520. SetPreset(current_preset_.preset_num - 1, true);
  521. }
  522.  
  523. // Go to the previous Preset skipping NewFont and Preon effects using FastOn.
  524. void previous_preset_fast() {
  525. #ifdef SAVE_PRESET
  526. SaveState(current_preset_.preset_num - 1);
  527. #endif
  528. SetPresetFast(current_preset_.preset_num - 1);
  529. }
  530.  
  531. // Rotates presets backwards and saves.
  532. virtual void rotate_presets() {
  533. #ifdef IDLE_OFF_TIME
  534. last_on_time_ = millis();
  535. #endif
  536. #ifdef ENABLE_AUDIO
  537. beeper.Beep(0.05, 2000.0);
  538. #endif
  539. LOCK_SD(true);
  540. current_preset_.Load(-1); // load last preset
  541. current_preset_.SaveAt(0); // save in first position, shifting all other presets down
  542. LOCK_SD(false);
  543. SetPreset(0, true);
  544. }
  545.  
  546. #ifdef BLADE_DETECT_PIN
  547. bool blade_detected_ = false;
  548. #endif
  549. bool system_booted = false; // bladein and bladeout sounds work with Blade ID, but not play over boot.wav.
  550.  
  551. // Measure and return the blade identifier resistor.
  552. float id(bool announce = false) {
  553. EnableBooster();
  554. BLADE_ID_CLASS_INTERNAL blade_id;
  555. float ret = blade_id.id();
  556.  
  557. if (announce) {
  558. PVLOG_STATUS << "BLADE ID: " << ret << "\n";
  559. // #ifdef SPEAK_BLADE_ID // BC method using sound_library for consistency of UI
  560. // #ifndef DISABLE_TALKIE
  561. // talkie.Say(spI);
  562. // talkie.Say(spD);
  563. // talkie.SayNumber((int)ret);
  564. // #else
  565. // if (&SFX_mnum) {
  566. // sound_library_.SayNumber(ret, SAY_WHOLE); // requires inclusion see up top
  567. // } else {
  568. // beeper.Beep(0.05, 2000.0);
  569. // }
  570. // #endif // DISABLE_TALKIE
  571. // #endif // SPEAK_BLADE_ID
  572. #ifdef SPEAK_BLADE_ID
  573. #ifdef DISABLE_TALKIE
  574. #error You cannot define both DISABLE_TALKIE and SPEAK_BLADE_ID
  575. #else
  576. talkie.Say(spI);
  577. talkie.Say(spD);
  578. talkie.SayNumber((int)ret);
  579. #endif // DISABLE_TALKIE
  580. #endif // SPEAK_BLADE_ID
  581. }
  582. #ifdef BLADE_DETECT_PIN
  583. if (!blade_detected_) {
  584. PVLOG_STATUS << "NO ";
  585. ret += NO_BLADE;
  586. }
  587. PVLOG_STATUS << "Blade Detected\n";
  588. #endif
  589. return ret;
  590. }
  591.  
  592. size_t FindBestConfig(bool announce = false) {
  593. static_assert(NELEM(blades) > 0, "blades array cannot be empty");
  594.  
  595. size_t best_config = 0;
  596. if (NELEM(blades) > 1) {
  597. float resistor = id(announce); // should this be available for scanid and id commands even if numblades = ?
  598.  
  599. float best_err = 100000000.0;
  600. for (size_t i = 0; i < NELEM(blades); i++) {
  601. float err = fabsf(resistor - blades[i].ohm);
  602. if (err < best_err) {
  603. best_config = i;
  604. best_err = err;
  605. }
  606. }
  607. }
  608. return best_config;
  609. }
  610.  
  611. #ifdef BLADE_ID_SCAN_MILLIS
  612. #ifndef SHARED_POWER_PINS
  613. #warning SHARED_POWER_PINS is recommended when using BLADE_ID_SCAN_MILLIS
  614. #endif
  615. bool find_blade_again_pending_ = false;
  616. uint32_t last_scan_id_ = 0;
  617. bool ScanBladeIdNow() {
  618. uint32_t now = millis();
  619. if (now - last_scan_id_ > BLADE_ID_SCAN_MILLIS) {
  620. last_scan_id_ = now;
  621. size_t best_config = FindBestConfig();
  622. if (current_config != blades + best_config) {
  623. // We can't call FindBladeAgain right away because
  624. // we're called from the blade. Wait until next loop() call.
  625. find_blade_again_pending_ = true;
  626. }
  627. return true;
  628. }
  629. return false;
  630. }
  631.  
  632. // Must be called from loop()
  633. void PollScanId() {
  634. if (find_blade_again_pending_) {
  635. find_blade_again_pending_ = false;
  636. FindBladeAgain();
  637. }
  638. }
  639. #else
  640. void PollScanId() {}
  641. #endif // BLADE_ID_SCAN_MILLIS
  642.  
  643. bool blade_inserted() { return current_config->id() < NO_BLADE; }
  644.  
  645. // Called from setup to identify the blade and select the right
  646. // Blade driver, style and sound font.
  647. void FindBlade(bool announce = false) {
  648. size_t best_config = FindBestConfig(announce);
  649. PVLOG_STATUS << "blade = " << best_config << "\n";
  650. current_config = blades + best_config;
  651. // set current blade status IN or OUT
  652. bool bladestatus = current_config->id() < NO_BLADE;
  653.  
  654. #define ACTIVATE(N) do { \
  655. if (!current_config->blade##N) goto bad_blade; \
  656. current_config->blade##N->Activate(); \
  657. } while(0);
  658.  
  659. ONCEPERBLADE(ACTIVATE);
  660. RestoreGlobalState();
  661. #ifdef SAVE_PRESET
  662. ResumePreset(); // ADDRESS THIS NEXT
  663. #else
  664. SetPreset(0, false); // Play font.wav (DoNewFont) here or not? set to announce if yes desired. I think just bladein/out sounds good.
  665. // Have Blade ID play bladein and bladeout sounds.
  666. #endif // SAVE_PRESET
  667. #ifndef BLADE_DETECT_PIN
  668. STDOUT << "**** Blade status = " << (bladestatus ? "IN" : "OUT") << "\n";
  669. if (system_booted) {
  670. SaberBase::DoBladeDetect(bladestatus);
  671. } else {
  672. STDOUT << "**** Booting = Not playing bladein/out\n";
  673. }
  674. #endif // BLADE_DETECT_PIN
  675. system_booted = true;
  676. return;
  677.  
  678. #if NUM_BLADES != 0
  679. bad_blade:
  680. ProffieOSErrors::error_in_blade_array();
  681. #endif
  682. }
  683.  
  684. SavePresetStateFile savestate_;
  685.  
  686. void ResumePreset() {
  687. savestate_.ReadINIFromSaveDir("curstate");
  688. SetPreset(savestate_.preset, false);
  689. // SaberBase::DoNewFont();
  690. }
  691.  
  692. // Blade length from config file.
  693. int GetMaxBladeLength(int blade) {
  694. #define GET_SINGLE_MAX_BLADE_LENGTH(N) if (blade == N) return current_config->blade##N->num_leds();
  695. ONCEPERBLADE(GET_SINGLE_MAX_BLADE_LENGTH)
  696. return 0;
  697. }
  698.  
  699. // If this returns -1 use GetMaxBladeLength()
  700. int GetBladeLength(int blade) {
  701. #ifdef DYNAMIC_BLADE_LENGTH
  702. #define GET_SINGLE_BLADE_LENGTH(N) if (blade == N) return savestate_.blade##N##len;
  703. ONCEPERBLADE(GET_SINGLE_BLADE_LENGTH)
  704. #endif
  705. return -1;
  706. }
  707.  
  708. // You'll need to reload the styles for this to take effect.
  709. void SetBladeLength(int blade, int len) {
  710. #ifdef DYNAMIC_BLADE_LENGTH
  711. #define SET_SINGLE_BLADE_LENGTH(N) if (blade == N) savestate_.blade##N##len = len;
  712. ONCEPERBLADE(SET_SINGLE_BLADE_LENGTH)
  713. #endif
  714. }
  715.  
  716. void SaveState(int preset) {
  717. PVLOG_NORMAL << "Saving Current Preset preset = " << preset << " savedir = " << GetSaveDir() << "\n";
  718. savestate_.preset = preset;
  719. savestate_.WriteToSaveDir("curstate");
  720. }
  721.  
  722. SaveGlobalStateFile saved_global_state;
  723. void RestoreGlobalState() {
  724. #if defined(SAVE_VOLUME) || defined(SAVE_BLADE_DIMMING) || defined(SAVE_CLASH_THRESHOLD)
  725. saved_global_state.ReadINIFromDir(NULL, "global");
  726.  
  727. #ifdef SAVE_CLASH_THRESHOLD
  728. SetClashThreshold(saved_global_state.clash_threshold);
  729. #endif
  730.  
  731. #ifdef SAVE_VOLUME
  732. if (saved_global_state.volume >= 0) {
  733. dynamic_mixer.set_volume(clampi32(saved_global_state.volume, 0, VOLUME));
  734. }
  735. #endif
  736.  
  737. #ifdef SAVE_BLADE_DIMMING
  738. SaberBase::SetDimming(saved_global_state.dimming);
  739. #endif
  740.  
  741. #endif
  742. }
  743.  
  744. void SaveGlobalState() {
  745. #if defined(SAVE_VOLUME) || defined(SAVE_BLADE_DIMMING) || defined(SAVE_CLASH_THRESHOLD)
  746. PVLOG_STATUS << "Saving Global State\n";
  747. #ifdef SAVE_CLASH_THRESHOLD
  748. saved_global_state.clash_threshold = GetCurrentClashThreshold();
  749. #endif
  750. #ifdef SAVE_VOLUME
  751. saved_global_state.volume = dynamic_mixer.get_volume();
  752. #endif
  753. #ifdef SAVE_BLADE_DIMMING
  754. saved_global_state.dimming = SaberBase::GetCurrentDimming();
  755. #endif
  756. saved_global_state.WriteToRootDir("global");
  757. #endif
  758. }
  759.  
  760. void FindBladeAgain() {
  761. if (!current_config) {
  762. // FindBlade() hasn't been called yet - ignore this.
  763. return;
  764. }
  765. // Reverse everything that FindBlade does.
  766.  
  767. // First free all styles, then allocate new ones to avoid memory
  768. // fragmentation.
  769. ONCEPERBLADE(UNSET_BLADE_STYLE)
  770.  
  771. #define DEACTIVATE(N) do { \
  772. if (current_config->blade##N) \
  773. current_config->blade##N->Deactivate(); \
  774. } while(0);
  775.  
  776. ONCEPERBLADE(DEACTIVATE);
  777. SaveVolumeIfNeeded();
  778. FindBlade(true);
  779. }
  780.  
  781. bool CheckInteractivePreon() {
  782. #define USES_INTERACTIVE_PREON(N) \
  783. if (current_config->blade##N->current_style() && current_config->blade##N->current_style()->IsHandled(HANDLED_FEATURE_INTERACTIVE_PREON)) return true;
  784. ONCEPERBLADE(USES_INTERACTIVE_PREON)
  785. return false;
  786. }
  787.  
  788. bool CheckInteractiveBlast() {
  789. #define USES_INTERACTIVE_BLAST(N) \
  790. if (current_config->blade##N->current_style() && current_config->blade##N->current_style()->IsHandled(HANDLED_FEATURE_INTERACTIVE_BLAST)) return true;
  791. ONCEPERBLADE(USES_INTERACTIVE_BLAST)
  792. return false;
  793. }
  794.  
  795. // Potentially called from interrupt!
  796. virtual void DoMotion(const Vec3& motion, bool clear) {
  797. fusor.DoMotion(motion, clear);
  798. }
  799.  
  800. // Potentially called from interrupt!
  801. virtual void DoAccel(const Vec3& accel, bool clear) {
  802. fusor.DoAccel(accel, clear);
  803. accel_loop_counter_.Update();
  804. Vec3 diff = fusor.clash_mss();
  805. float v;
  806. if (clear) {
  807. accel_ = accel;
  808. diff = Vec3(0,0,0);
  809. v = 0.0;
  810. } else {
  811. #ifndef PROFFIEOS_DONT_USE_GYRO_FOR_CLASH
  812. v = (diff.len() + fusor.gyro_clash_value()) / 2.0;
  813. #else
  814. v = diff.len();
  815. #endif
  816. }
  817. #if 0
  818. static uint32_t last_printout=0;
  819. if (millis() - last_printout > 1000) {
  820. last_printout = millis();
  821. STDOUT << "ACCEL: " << accel
  822. << " diff: " << diff
  823. << " gyro: " << fusor.gyro_clash_value()
  824. << " v = " << v << "\n";
  825. }
  826. #endif
  827. // If we're spinning the saber or if loud sounds are playing,
  828. // require a stronger acceleration to activate the clash.
  829. if (v > (CLASH_THRESHOLD_G + fusor.gyro().len() / 200.0)
  830. #if defined(ENABLE_AUDIO) && defined(AUDIO_CLASH_SUPPRESSION_LEVEL)
  831. + (dynamic_mixer.audio_volume() * (AUDIO_CLASH_SUPPRESSION_LEVEL * 0.000001))
  832. #endif
  833. ) {
  834. if ( (accel_ - fusor.down()).len2() > (accel - fusor.down()).len2() ) {
  835. diff = -diff;
  836. }
  837. bool stab = diff.x < - 2.0 * sqrtf(diff.y * diff.y + diff.z * diff.z) &&
  838. fusor.swing_speed() < 150;
  839.  
  840. if (clash_pending1_) {
  841. pending_clash_strength1_ = std::max<float>(v, (float)pending_clash_strength1_);
  842. } else {
  843. clash_pending1_ = true;
  844. pending_clash_is_stab1_ = stab;
  845. pending_clash_strength1_ = v;
  846. }
  847. }
  848. accel_ = accel;
  849. }
  850.  
  851. void SB_Top(uint64_t total_cycles) override {
  852. STDOUT.print("Acceleration measurements per second: ");
  853. accel_loop_counter_.Print();
  854. STDOUT.println("");
  855. }
  856.  
  857. enum StrokeType {
  858. TWIST_CLOSE,
  859. TWIST_LEFT,
  860. TWIST_RIGHT,
  861.  
  862. SHAKE_CLOSE,
  863. SHAKE_FWD,
  864. SHAKE_REW
  865. };
  866. struct Stroke {
  867. StrokeType type;
  868. uint32_t start_millis;
  869. uint32_t end_millis;
  870. uint32_t length() const { return end_millis - start_millis; }
  871. };
  872.  
  873. Stroke strokes[5];
  874.  
  875. void MonitorStrokes() {
  876. if (monitor.IsMonitoring(Monitoring::MonitorStrokes)) {
  877. STDOUT.print("Stroke: ");
  878. switch (strokes[NELEM(strokes)-1].type) {
  879. case TWIST_LEFT:
  880. STDOUT.print("TwistLeft");
  881. break;
  882. case TWIST_RIGHT:
  883. STDOUT.print("TwistRight");
  884. break;
  885. case SHAKE_FWD:
  886. STDOUT.print("Thrust");
  887. break;
  888. case SHAKE_REW:
  889. STDOUT.print("Yank");
  890. break;
  891. default: break;
  892. }
  893. STDOUT << " len = " << strokes[NELEM(strokes)-1].length();
  894. uint32_t separation =
  895. strokes[NELEM(strokes)-1].start_millis -
  896. strokes[NELEM(strokes)-2].end_millis;
  897. STDOUT << " separation=" << separation
  898. << " mss=" << fusor.mss()
  899. << " swspd=" << fusor.swing_speed()
  900. << "\n";
  901. }
  902. }
  903.  
  904. StrokeType GetStrokeGroup(StrokeType a) {
  905. switch (a) {
  906. case TWIST_CLOSE:
  907. case TWIST_LEFT:
  908. case TWIST_RIGHT:
  909. return TWIST_CLOSE;
  910. case SHAKE_CLOSE:
  911. case SHAKE_FWD:
  912. case SHAKE_REW:
  913. break;
  914. }
  915. return SHAKE_CLOSE;
  916. }
  917.  
  918. bool ShouldClose(StrokeType a, StrokeType b) {
  919. // Don't close if it's the same exact stroke
  920. if (a == b) return false;
  921. // Different stroke in same stroke group -> close
  922. if (GetStrokeGroup(a) == GetStrokeGroup(b)) return true;
  923. // New stroke in different group -> close
  924. if (GetStrokeGroup(b) != b) return true;
  925. return false;
  926. }
  927.  
  928. bool DoGesture(StrokeType gesture) {
  929. if (gesture == strokes[NELEM(strokes)-1].type) {
  930. if (strokes[NELEM(strokes)-1].end_millis == 0) {
  931. // Stroke not done, wait.
  932. return false;
  933. }
  934. if (millis() - strokes[NELEM(strokes)-1].end_millis < 50) {
  935. // Stroke continues
  936. strokes[NELEM(strokes)-1].end_millis = millis();
  937. return false;
  938. }
  939. }
  940. if (strokes[NELEM(strokes) - 1].end_millis == 0 &&
  941. GetStrokeGroup(gesture) == GetStrokeGroup(strokes[NELEM(strokes) - 1].type)) {
  942. strokes[NELEM(strokes) - 1].end_millis = millis();
  943. MonitorStrokes();
  944. return true;
  945. }
  946. // Exit here if it's a *_CLOSE stroke.
  947. if (GetStrokeGroup(gesture) == gesture) return false;
  948. // If last stroke is very short, just write over it.
  949. if (strokes[NELEM(strokes)-1].end_millis -
  950. strokes[NELEM(strokes)-1].start_millis > 10) {
  951. for (size_t i = 0; i < NELEM(strokes) - 1; i++) {
  952. strokes[i] = strokes[i+1];
  953. }
  954. }
  955. strokes[NELEM(strokes)-1].type = gesture;
  956. strokes[NELEM(strokes)-1].start_millis = millis();
  957. strokes[NELEM(strokes)-1].end_millis = 0;
  958. return false;
  959. }
  960.  
  961. // The prop should call this from Loop() if it wants to detect twists.
  962. void DetectTwist() {
  963. Vec3 gyro = fusor.gyro();
  964. bool process = false;
  965. if (fabsf(gyro.x) > 200.0 &&
  966. fabsf(gyro.x) > 3.0f * abs(gyro.y) &&
  967. fabsf(gyro.x) > 3.0f * abs(gyro.z)) {
  968. process = DoGesture(gyro.x > 0 ? TWIST_LEFT : TWIST_RIGHT);
  969. } else {
  970. process = DoGesture(TWIST_CLOSE);
  971. }
  972. if (process) {
  973. if ((strokes[NELEM(strokes)-1].type == TWIST_LEFT &&
  974. strokes[NELEM(strokes)-2].type == TWIST_RIGHT) ||
  975. (strokes[NELEM(strokes)-1].type == TWIST_RIGHT &&
  976. strokes[NELEM(strokes)-2].type == TWIST_LEFT)) {
  977. if (strokes[NELEM(strokes) -1].length() > 90UL &&
  978. strokes[NELEM(strokes) -1].length() < 300UL &&
  979. strokes[NELEM(strokes) -2].length() > 90UL &&
  980. strokes[NELEM(strokes) -2].length() < 300UL) {
  981. uint32_t separation =
  982. strokes[NELEM(strokes)-1].start_millis -
  983. strokes[NELEM(strokes)-2].end_millis;
  984. if (separation < 200UL) {
  985. STDOUT.println("TWIST");
  986. // We have a twisting gesture.
  987. Event(BUTTON_NONE, EVENT_TWIST);
  988. }
  989. }
  990. }
  991. }
  992. }
  993.  
  994. // The prop should call this from Loop() if it wants to detect shakes.
  995. void DetectShake() {
  996. Vec3 mss = fusor.mss();
  997. bool process = false;
  998. if (mss.y * mss.y + mss.z * mss.z < 16.0 &&
  999. (mss.x > 7 || mss.x < -6) &&
  1000. fusor.swing_speed() < 150) {
  1001. process = DoGesture(mss.x > 0 ? SHAKE_FWD : SHAKE_REW);
  1002. } else {
  1003. process = DoGesture(SHAKE_CLOSE);
  1004. }
  1005. if (process) {
  1006. int i;
  1007. for (i = 0; i < 5; i++) {
  1008. if (strokes[NELEM(strokes)-1-i].type !=
  1009. ((i & 1) ? SHAKE_REW : SHAKE_FWD)) break;
  1010. if (i) {
  1011. uint32_t separation =
  1012. strokes[NELEM(strokes)-i].start_millis -
  1013. strokes[NELEM(strokes)-1-i].end_millis;
  1014. if (separation > 250) break;
  1015. }
  1016. }
  1017. if (i == 5) {
  1018. strokes[NELEM(strokes)-1].type = SHAKE_CLOSE;
  1019. Event(BUTTON_NONE, EVENT_SHAKE);
  1020. }
  1021. }
  1022. }
  1023.  
  1024. bool swinging_ = false;
  1025. // The prop should call this from Loop() if it wants to detect swings as an event.
  1026. void DetectSwing() {
  1027. if (!swinging_ && fusor.swing_speed() > 250) {
  1028. swinging_ = true;
  1029. Event(BUTTON_NONE, EVENT_SWING);
  1030. }
  1031. if (swinging_ && fusor.swing_speed() < 100) {
  1032. swinging_ = false;
  1033. }
  1034. }
  1035.  
  1036. void SB_Motion(const Vec3& gyro, bool clear) override {
  1037. if (monitor.ShouldPrint(Monitoring::MonitorGyro)) {
  1038. // Got gyro data
  1039. STDOUT.print("GYRO: ");
  1040. STDOUT.print(gyro.x);
  1041. STDOUT.print(", ");
  1042. STDOUT.print(gyro.y);
  1043. STDOUT.print(", ");
  1044. STDOUT.println(gyro.z);
  1045. }
  1046. }
  1047.  
  1048. Vec3 accel_;
  1049.  
  1050. void StartOrStopTrack() {
  1051. #ifdef ENABLE_AUDIO
  1052. if (track_player_) {
  1053. track_player_->set_fade_time(1.0);
  1054. track_player_->FadeAndStop();
  1055. track_player_.Free();
  1056. } else {
  1057. MountSDCard();
  1058. EnableAmplifier();
  1059. track_player_ = GetFreeWavPlayer();
  1060. if (track_player_) {
  1061. track_player_->Play(current_preset_.track.get());
  1062. } else {
  1063. STDOUT.println("No available WAV players.");
  1064. }
  1065. }
  1066. #else
  1067. STDOUT.println("Audio disabled.");
  1068. #endif
  1069. }
  1070.  
  1071. // // S3 MOD Starts the Menu Hum for Preset Menu/Soundfont Selection
  1072. // void StartMenuBackgrd()
  1073. // {
  1074. // #ifdef ENABLE_AUDIO
  1075. // MountSDCard();
  1076. // EnableAmplifier();
  1077. // track_player_ = GetFreeWavPlayer();
  1078. // if (track_player_)
  1079. // {
  1080. // track_player_->Play("common/fontmenuback.wav");
  1081. // } else
  1082. // {
  1083. // STDOUT.println("No available WAV players.");
  1084. // }
  1085.  
  1086. // #else
  1087. // STDOUT.println("Audio disabled.");
  1088. // #endif
  1089. // }
  1090.  
  1091.  
  1092. void ListTracks(const char* dir) {
  1093. if (!LSFS::Exists(dir)) return;
  1094. for (LSFS::Iterator i2(dir); i2; ++i2) {
  1095. if (endswith(".wav", i2.name()) && i2.size() > 200000) {
  1096. STDOUT << dir << "/" << i2.name() << "\n";
  1097. }
  1098. }
  1099. }
  1100.  
  1101. virtual void LowBatteryOff() {
  1102. if (SaberBase::IsOn()) {
  1103. STDOUT.print("Battery low, turning off. Battery voltage: ");
  1104. STDOUT.println(battery_monitor.battery());
  1105. Off();
  1106. }
  1107. }
  1108.  
  1109. virtual void CheckLowBattery() {
  1110. if (battery_monitor.low()) {
  1111. if (current_style() && !current_style()->Charging()) {
  1112. LowBatteryOff();
  1113. if (millis() - last_beep_ > 15000) { // (was 5000)
  1114. STDOUT << "Low battery: " << battery_monitor.battery() << " volts\n";
  1115. SaberBase::DoLowBatt();
  1116. last_beep_ = millis();
  1117. }
  1118. }
  1119. }
  1120. }
  1121.  
  1122.  
  1123. uint32_t last_motion_call_millis_;
  1124. void CallMotion() {
  1125. if (millis() == last_motion_call_millis_) return;
  1126. if (!fusor.ready()) return;
  1127. bool clear = millis() - last_motion_call_millis_ > 100;
  1128. last_motion_call_millis_ = millis();
  1129. SaberBase::DoAccel(fusor.accel(), clear);
  1130. SaberBase::DoMotion(fusor.gyro(), clear);
  1131.  
  1132. if (monitor.ShouldPrint(Monitoring::MonitorClash)) {
  1133. STDOUT << "ACCEL: " << fusor.accel() << "\n";
  1134. }
  1135. }
  1136. volatile bool clash_pending1_ = false;
  1137. volatile bool pending_clash_is_stab1_ = false;
  1138. volatile float pending_clash_strength1_ = 0.0;
  1139. bool idle_off_time_reported_ = false; // BC IDLE_OFF_TIME expired printout only once mod
  1140.  
  1141. uint32_t last_beep_;
  1142. float current_tick_angle_ = 0.0;
  1143.  
  1144. bool interrupt_clash_pending() const {
  1145. return clash_pending1_;
  1146. }
  1147.  
  1148. void Loop() override {
  1149. CallMotion();
  1150. if (clash_pending1_) {
  1151. clash_pending1_ = false;
  1152. Clash(pending_clash_is_stab1_, pending_clash_strength1_);
  1153. }
  1154. if (clash_pending_ && millis() - last_clash_ >= clash_timeout_) {
  1155. clash_pending_ = false;
  1156. Clash2(pending_clash_is_stab_, pending_clash_strength_);
  1157. }
  1158. PollScanId();
  1159. CheckLowBattery();
  1160.  
  1161. #ifdef ENABLE_AUDIO
  1162. if (track_player_ && !track_player_->isPlaying()) {
  1163. track_player_.Free();
  1164. }
  1165. #endif
  1166.  
  1167. #ifndef DISABLE_COLOR_CHANGE
  1168. #define TICK_ANGLE (M_PI * 2 / 12)
  1169. switch (SaberBase::GetColorChangeMode()) {
  1170. case SaberBase::COLOR_CHANGE_MODE_NONE:
  1171. break;
  1172. case SaberBase::COLOR_CHANGE_MODE_STEPPED: {
  1173. float a = fusor.angle2() - current_tick_angle_;
  1174. if (a > M_PI) a-=M_PI*2;
  1175. if (a < -M_PI) a+=M_PI*2;
  1176. if (a > TICK_ANGLE * 2/3) {
  1177. current_tick_angle_ += TICK_ANGLE;
  1178. if (current_tick_angle_ > M_PI) current_tick_angle_ -= M_PI * 2;
  1179. STDOUT << "TICK+\n";
  1180. SaberBase::UpdateVariation(1);
  1181. }
  1182. if (a < -TICK_ANGLE * 2/3) {
  1183. current_tick_angle_ -= TICK_ANGLE;
  1184. if (current_tick_angle_ < M_PI) current_tick_angle_ += M_PI * 2;
  1185. STDOUT << "TICK-\n";
  1186. SaberBase::UpdateVariation(-1);
  1187. }
  1188. break;
  1189. }
  1190. case SaberBase::COLOR_CHANGE_MODE_ZOOMED: {
  1191. #define ZOOM_ANGLE (M_PI * 2 / 2000)
  1192. float a = fusor.angle2() - current_tick_angle_;
  1193. if (a > M_PI) a-=M_PI*2;
  1194. if (a < -M_PI) a+=M_PI*2;
  1195. int steps = (int)floor(fabs(a) / ZOOM_ANGLE - 0.3);
  1196. if (steps < 0) steps = 0;
  1197. if (a < 0) steps = -steps;
  1198. current_tick_angle_ += ZOOM_ANGLE * steps;
  1199. SaberBase::SetVariation(0x7fff & (SaberBase::GetCurrentVariation() + steps));
  1200. break;
  1201. }
  1202. case SaberBase::COLOR_CHANGE_MODE_SMOOTH:
  1203. float a = fmodf(fusor.angle2() - current_tick_angle_, M_PI * 2);
  1204. SaberBase::SetVariation(0x7fff & (int32_t)(a * (32768 / (M_PI * 2))));
  1205. break;
  1206. }
  1207. if (monitor.ShouldPrint(Monitoring::MonitorVariation)) {
  1208. STDOUT << " variation = " << SaberBase::GetCurrentVariation()
  1209. << " ccmode = " << SaberBase::GetColorChangeMode()
  1210. // << " color = " << current_config->blade1->current_style()->getColor(0)
  1211. << "\n";
  1212. }
  1213. #endif
  1214.  
  1215.  
  1216. #ifdef IDLE_OFF_TIME
  1217. if (SaberBase::IsOn() ||
  1218. (current_style() && current_style()->Charging())) {
  1219. last_on_time_ = millis();
  1220. idle_off_time_reported_ = false; // BC IDLE_OFF_TIME expired printout only once mod
  1221. }
  1222. if (millis() - last_on_time_ >= IDLE_OFF_TIME) {
  1223. if (!idle_off_time_reported_) { // BC IDLE_OFF_TIME expired printout only once mod
  1224. STDERR << "Time = " << millis() - last_on_time_ << ". IDLE_OFF_TIME expired.\n";
  1225. idle_off_time_reported_ = true;
  1226. }
  1227. SaberBase::DoOff(OFF_IDLE);
  1228. last_on_time_ = millis();
  1229. }
  1230. #endif
  1231.  
  1232. PollSaveColorChange();
  1233. }
  1234.  
  1235. #ifdef IDLE_OFF_TIME
  1236. uint32_t last_on_time_;
  1237. #endif
  1238.  
  1239. #ifndef DISABLE_COLOR_CHANGE
  1240. void ToggleColorChangeMode() {
  1241. if (!current_style()) return;
  1242. if (SaberBase::GetColorChangeMode() == SaberBase::COLOR_CHANGE_MODE_NONE) {
  1243. current_tick_angle_ = fusor.angle2();
  1244. bool handles_color_change = false;
  1245. #define CHECK_SUPPORTS_COLOR_CHANGE(N) \
  1246. handles_color_change |= current_config->blade##N->current_style() && current_config->blade##N->current_style()->IsHandled(HANDLED_FEATURE_CHANGE_TICKED);
  1247. ONCEPERBLADE(CHECK_SUPPORTS_COLOR_CHANGE)
  1248. if (!handles_color_change) {
  1249. PVLOG_NORMAL << "Entering smooth color change mode.\n";
  1250. current_tick_angle_ -= SaberBase::GetCurrentVariation() * M_PI * 2 / 32768;
  1251. current_tick_angle_ = fmodf(current_tick_angle_, M_PI * 2);
  1252.  
  1253. SaberBase::SetColorChangeMode(SaberBase::COLOR_CHANGE_MODE_SMOOTH);
  1254. } else {
  1255. #ifdef COLOR_CHANGE_DIRECT
  1256. PVLOG_NORMAL << "Color change, TICK+\n";
  1257. SaberBase::UpdateVariation(1);
  1258. #else
  1259. PVLOG_NORMAL << "Entering stepped color change mode.\n";
  1260. SaberBase::SetColorChangeMode(SaberBase::COLOR_CHANGE_MODE_STEPPED);
  1261. #endif
  1262. }
  1263. } else {
  1264. PVLOG_NORMAL << "Color change mode done, variation = " << SaberBase::GetCurrentVariation() << "\n";
  1265. SaberBase::SetColorChangeMode(SaberBase::COLOR_CHANGE_MODE_NONE);
  1266. }
  1267. }
  1268. #endif // DISABLE_COLOR_CHANGE
  1269.  
  1270. virtual void PrintButton(uint32_t b) {
  1271. if (b & BUTTON_POWER) STDOUT.print("Power");
  1272. if (b & BUTTON_AUX) STDOUT.print("Aux");
  1273. if (b & BUTTON_AUX2) STDOUT.print("Aux2");
  1274. if (b & BUTTON_UP) STDOUT.print("Up");
  1275. if (b & BUTTON_DOWN) STDOUT.print("Down");
  1276. if (b & BUTTON_LEFT) STDOUT.print("Left");
  1277. if (b & BUTTON_RIGHT) STDOUT.print("Right");
  1278. if (b & BUTTON_SELECT) STDOUT.print("Select");
  1279. if (b & BUTTON_BLADE_DETECT) STDOUT.print("Select");
  1280. if (b & MODE_ON) STDOUT.print("On");
  1281. }
  1282.  
  1283. void PrintEvent(uint32_t e) {
  1284. int cnt = 0;
  1285. if (e >= EVENT_FIRST_PRESSED &&
  1286. e <= EVENT_FOURTH_CLICK_LONG) {
  1287. cnt = (e - EVENT_PRESSED) / (EVENT_SECOND_PRESSED - EVENT_FIRST_PRESSED);
  1288. e -= (EVENT_SECOND_PRESSED - EVENT_FIRST_PRESSED) * cnt;
  1289. }
  1290. switch (e) {
  1291. case EVENT_NONE: STDOUT.print("None"); break;
  1292. case EVENT_PRESSED: STDOUT.print("Pressed"); break;
  1293. case EVENT_RELEASED: STDOUT.print("Released"); break;
  1294. case EVENT_HELD: STDOUT.print("Held"); break;
  1295. case EVENT_HELD_MEDIUM: STDOUT.print("HeldMedium"); break;
  1296. case EVENT_HELD_LONG: STDOUT.print("HeldLong"); break;
  1297. case EVENT_CLICK_SHORT: STDOUT.print("Shortclick"); break;
  1298. case EVENT_CLICK_LONG: STDOUT.print("Longclick"); break;
  1299. case EVENT_SAVED_CLICK_SHORT: STDOUT.print("SavedShortclick"); break;
  1300. case EVENT_LATCH_ON: STDOUT.print("On"); break;
  1301. case EVENT_LATCH_OFF: STDOUT.print("Off"); break;
  1302. case EVENT_STAB: STDOUT.print("Stab"); break;
  1303. case EVENT_SWING: STDOUT.print("Swing"); break;
  1304. case EVENT_SHAKE: STDOUT.print("Shake"); break;
  1305. case EVENT_TWIST: STDOUT.print("Twist"); break;
  1306. case EVENT_CLASH: STDOUT.print("Clash"); break;
  1307. case EVENT_THRUST: STDOUT.print("Thrust"); break;
  1308. case EVENT_PUSH: STDOUT.print("Push"); break;
  1309. default: STDOUT.print("?"); STDOUT.print(e); break;
  1310. }
  1311. if (cnt) {
  1312. STDOUT.print('#');
  1313. STDOUT.print(cnt);
  1314. }
  1315. }
  1316.  
  1317. void PrintEvent(enum BUTTON button, EVENT event) {
  1318. STDOUT.print("EVENT: ");
  1319. if (button) {
  1320. PrintButton(button);
  1321. STDOUT.print("-");
  1322. }
  1323. PrintEvent(event);
  1324. if (current_modifiers & ~button) {
  1325. STDOUT.print(" mods ");
  1326. PrintButton(current_modifiers);
  1327. }
  1328. if (IsOn()) STDOUT.print(" ON");
  1329. STDOUT.print(" millis=");
  1330. STDOUT.println(millis());
  1331. }
  1332.  
  1333. bool Parse(const char *cmd, const char* arg) override {
  1334. if (!strcmp(cmd, "id")) {
  1335. id(true);
  1336. return true;
  1337. }
  1338. if (!strcmp(cmd, "scanid")) {
  1339. FindBladeAgain();
  1340. return true;
  1341. }
  1342. if (!strcmp(cmd, "on") || !strcmp(cmd, "out")) { // BC add
  1343. On();
  1344. return true;
  1345. }
  1346. if (!strcmp(cmd, "off") || !strcmp(cmd, "in")) { // BC add
  1347. Off();
  1348. return true;
  1349. }
  1350. if (!strcmp(cmd, "get_on")) {
  1351. STDOUT.println(IsOn());
  1352. return true;
  1353. }
  1354. if (!strcmp(cmd, "clash")) {
  1355. Clash2(false, 10.0);
  1356. return true;
  1357. }
  1358. if (!strcmp(cmd, "stab")) {
  1359. Clash2(true, 10.0);
  1360. return true;
  1361. }
  1362. if (!strcmp(cmd, "force")) {
  1363. SaberBase::DoForce();
  1364. return true;
  1365. }
  1366. if (!strcmp(cmd, "blast")) {
  1367. // Avoid the base and the very tip.
  1368. // TODO: Make blast only appear on one blade!
  1369. SaberBase::DoBlast();
  1370. return true;
  1371. }
  1372. if (!strcmp(cmd, "lock") || !strcmp(cmd, "lockup")) {
  1373. STDOUT.print("Lockup ");
  1374. if (SaberBase::Lockup() == SaberBase::LOCKUP_NONE) {
  1375. SaberBase::SetLockup(SaberBase::LOCKUP_NORMAL);
  1376. SaberBase::DoBeginLockup();
  1377. STDOUT.println("ON");
  1378. } else {
  1379. SaberBase::DoEndLockup();
  1380. SaberBase::SetLockup(SaberBase::LOCKUP_NONE);
  1381. STDOUT.println("OFF");
  1382. }
  1383. return true;
  1384. }
  1385. if (!strcmp(cmd, "drag")) {
  1386. STDOUT.print("Drag ");
  1387. if (SaberBase::Lockup() == SaberBase::LOCKUP_NONE) {
  1388. SaberBase::SetLockup(SaberBase::LOCKUP_DRAG);
  1389. SaberBase::DoBeginLockup();
  1390. STDOUT.println("ON");
  1391. } else {
  1392. SaberBase::DoEndLockup();
  1393. SaberBase::SetLockup(SaberBase::LOCKUP_NONE);
  1394. STDOUT.println("OFF");
  1395. }
  1396. return true;
  1397. }
  1398. if (!strcmp(cmd, "lblock") || !strcmp(cmd, "lb")) {
  1399. STDOUT.print("lblock ");
  1400. if (SaberBase::Lockup() == SaberBase::LOCKUP_NONE) {
  1401. SaberBase::SetLockup(SaberBase::LOCKUP_LIGHTNING_BLOCK);
  1402. SaberBase::DoBeginLockup();
  1403. STDOUT.println("ON");
  1404. } else {
  1405. SaberBase::DoEndLockup();
  1406. SaberBase::SetLockup(SaberBase::LOCKUP_NONE);
  1407. STDOUT.println("OFF");
  1408. }
  1409. return true;
  1410. }
  1411. if (!strcmp(cmd, "melt")) {
  1412. STDOUT.print("melt ");
  1413. if (SaberBase::Lockup() == SaberBase::LOCKUP_NONE) {
  1414. SaberBase::SetLockup(SaberBase::LOCKUP_MELT);
  1415. SaberBase::DoBeginLockup();
  1416. STDOUT.println("ON");
  1417. } else {
  1418. SaberBase::DoEndLockup();
  1419. SaberBase::SetLockup(SaberBase::LOCKUP_NONE);
  1420. STDOUT.println("OFF");
  1421. }
  1422. return true;
  1423. }
  1424.  
  1425. #ifdef ENABLE_AUDIO
  1426.  
  1427. #ifndef DISABLE_DIAGNOSTIC_COMMANDS
  1428. if (!strcmp(cmd, "beep")) {
  1429. // Close Encounters
  1430. // beeper.Beep(0.5, 293.66 * 2);
  1431. // beeper.Beep(0.5, 329.33 * 2);
  1432. // beeper.Beep(0.5, 261.63 * 2);
  1433. // beeper.Beep(0.5, 130.81 * 2);
  1434. // beeper.Beep(1.0, 196.00 * 2);
  1435.  
  1436. // Imperial March // BC added songs :)
  1437. // beeper.Beep(0.45, 195);
  1438. // beeper.Beep(0.2, 0);
  1439. // beeper.Beep(0.45, 195);
  1440. // beeper.Beep(0.2, 0);
  1441. // beeper.Beep(0.45, 195);
  1442. // beeper.Beep(0.2, 0);
  1443. // beeper.Beep(0.45, 155);
  1444. // beeper.Beep(0.15, 233);
  1445. // beeper.Beep(0.45, 195);
  1446. // beeper.Beep(0.2, 0);
  1447. // beeper.Beep(0.45, 155);
  1448. // beeper.Beep(0.15, 233);
  1449. // beeper.Beep(0.6, 195);
  1450.  
  1451. // Star Wars Theme
  1452. beeper.Beep(1.0, 475);
  1453. beeper.Beep(0.5, 693);
  1454. beeper.Beep(0.16, 625);
  1455. beeper.Beep(0.16, 595);
  1456. beeper.Beep(0.16, 525);
  1457. beeper.Beep(1.1, 950);
  1458. beeper.Beep(0.5, 693);
  1459. beeper.Beep(0.16, 625);
  1460. beeper.Beep(0.16, 595);
  1461. beeper.Beep(0.16, 525);
  1462. beeper.Beep(1.1, 950);
  1463. beeper.Beep(0.5, 693);
  1464. beeper.Beep(0.16, 625);
  1465. beeper.Beep(0.16, 595);
  1466. beeper.Beep(0.16, 625);
  1467. beeper.Beep(1.1, 525);
  1468. return true;
  1469. }
  1470. #endif
  1471. #ifdef ENABLE_DEVELOPER_COMMANDS
  1472. if (!strcmp(cmd, "sd_card_not_found")) {
  1473. ProffieOSErrors::sd_card_not_found();
  1474. return true;
  1475. }
  1476. if (!strcmp(cmd, "font_directory_not_found")) {
  1477. ProffieOSErrors::font_directory_not_found();
  1478. return true;
  1479. }
  1480. if (!strcmp(cmd, "error_in_blade_array")) {
  1481. ProffieOSErrors::error_in_blade_array();
  1482. return true;
  1483. }
  1484. if (!strcmp(cmd, "error_in_font_directory")) {
  1485. ProffieOSErrors::error_in_font_directory();
  1486. return true;
  1487. }
  1488. if (!strcmp(cmd, "low_battery")) {
  1489. SaberBase::DoLowBatt();
  1490. return true;
  1491. }
  1492. #endif
  1493. if (!strcmp(cmd, "play")) {
  1494. if (!arg) {
  1495. StartOrStopTrack();
  1496. return true;
  1497. }
  1498. MountSDCard();
  1499. EnableAmplifier();
  1500. RefPtr<BufferedWavPlayer> player = GetFreeWavPlayer();
  1501. if (player) {
  1502. STDOUT.print("Playing ");
  1503. STDOUT.println(arg);
  1504. if (!player->PlayInCurrentDir(arg))
  1505. player->Play(arg);
  1506. } else {
  1507. STDOUT.println("No available WAV players.");
  1508. }
  1509. return true;
  1510. }
  1511. if (!strcmp(cmd, "play_track")) {
  1512. if (!arg) {
  1513. StartOrStopTrack();
  1514. return true;
  1515. }
  1516. if (track_player_) {
  1517. track_player_->Stop();
  1518. track_player_.Free();
  1519. }
  1520. MountSDCard();
  1521. EnableAmplifier();
  1522. track_player_ = GetFreeWavPlayer();
  1523. if (track_player_) {
  1524. STDOUT.print("Playing ");
  1525. STDOUT.println(arg);
  1526. if (!track_player_->PlayInCurrentDir(arg))
  1527. track_player_->Play(arg);
  1528. } else {
  1529. STDOUT.println("No available WAV players.");
  1530. }
  1531. return true;
  1532. }
  1533. if (!strcmp(cmd, "stop_track")) {
  1534. if (track_player_) {
  1535. track_player_->Stop();
  1536. track_player_.Free();
  1537. }
  1538. return true;
  1539. }
  1540. if (!strcmp(cmd, "get_track")) {
  1541. if (track_player_) {
  1542. STDOUT.println(track_player_->Filename());
  1543. }
  1544. return true;
  1545. }
  1546. #ifndef DISABLE_DIAGNOSTIC_COMMANDS
  1547. if (!strcmp(cmd, "volumes")) {
  1548. for (size_t unit = 0; unit < NELEM(wav_players); unit++) {
  1549. STDOUT.print(" Unit ");
  1550. STDOUT.print(unit);
  1551. STDOUT.print(" Volume ");
  1552. STDOUT.println(wav_players[unit].volume());
  1553. }
  1554. return true;
  1555. }
  1556. #endif
  1557. #ifndef DISABLE_DIAGNOSTIC_COMMANDS
  1558. if (!strcmp(cmd, "buffered")) {
  1559. for (size_t unit = 0; unit < NELEM(wav_players); unit++) {
  1560. STDOUT.print(" Unit ");
  1561. STDOUT.print(unit);
  1562. STDOUT.print(" Buffered: ");
  1563. STDOUT.println(wav_players[unit].buffered());
  1564. }
  1565. return true;
  1566. }
  1567. #endif
  1568.  
  1569. #endif // enable sound
  1570. if (!strcmp(cmd, "cd")) {
  1571. chdir(arg);
  1572. SaberBase::DoNewFont();
  1573. return true;
  1574. }
  1575. #if 0
  1576. if (!strcmp(cmd, "mkdir")) {
  1577. SD.mkdir(arg);
  1578. return true;
  1579. }
  1580. #endif
  1581. if (!strcmp(cmd, "pwd")) {
  1582. for (const char* dir = current_directory; dir; dir = next_current_directory(dir)) {
  1583. STDOUT.println(dir);
  1584. }
  1585. return true;
  1586. }
  1587. if (!strcmp(cmd, "n") || (!strcmp(cmd, "next") && arg && (!strcmp(arg, "preset") || !strcmp(arg, "pre")))) {
  1588. next_preset();
  1589. return true;
  1590. }
  1591. if (!strcmp(cmd, "p") || (!strcmp(cmd, "prev") && arg && (!strcmp(arg, "preset") || !strcmp(arg, "pre")))) {
  1592. previous_preset();
  1593. return true;
  1594. }
  1595. if (!strcmp(cmd, "rotate")) {
  1596. rotate_presets();
  1597. return true;
  1598. }
  1599.  
  1600. if (!strcmp(cmd, "list_presets")) {
  1601. CurrentPreset tmp;
  1602. for (int i = 0; ; i++) {
  1603. tmp.SetPreset(i);
  1604. if (tmp.preset_num != i) break;
  1605. tmp.Print();
  1606. }
  1607. return true;
  1608. }
  1609.  
  1610. if (!strcmp(cmd, "set_font") && arg) {
  1611. current_preset_.font = mkstr(arg);
  1612. current_preset_.Save();
  1613. return true;
  1614. }
  1615.  
  1616. if (!strcmp(cmd, "set_track") && arg) {
  1617. current_preset_.track = mkstr(arg);
  1618. current_preset_.Save();
  1619. return true;
  1620. }
  1621.  
  1622. if (!strcmp(cmd, "set_name") && arg) {
  1623. current_preset_.name = mkstr(arg);
  1624. current_preset_.Save();
  1625. return true;
  1626. }
  1627.  
  1628. #define SET_STYLE_CMD(N) \
  1629. if (!strcmp(cmd, "set_style" #N) && arg) { \
  1630. current_preset_.current_style_[N-1] = mkstr(arg); \
  1631. current_preset_.Save(); \
  1632. return true; \
  1633. }
  1634. ONCEPERBLADE(SET_STYLE_CMD)
  1635.  
  1636. if (!strcmp(cmd, "move_preset") && arg) {
  1637. int32_t pos = strtol(arg, NULL, 0);
  1638. current_preset_.SaveAt(pos);
  1639. return true;
  1640. }
  1641.  
  1642. if (!strcmp(cmd, "duplicate_preset") && arg) {
  1643. int32_t pos = strtol(arg, NULL, 0);
  1644. current_preset_.preset_num = -1;
  1645. current_preset_.SaveAt(pos);
  1646. return true;
  1647. }
  1648.  
  1649. if (!strcmp(cmd, "delete_preset") && arg) {
  1650. current_preset_.SaveAt(-1);
  1651. return true;
  1652. }
  1653.  
  1654. if (!strcmp(cmd, "show_current_preset")) {
  1655. current_preset_.Print();
  1656. return true;
  1657. }
  1658.  
  1659. #ifdef DYNAMIC_BLADE_LENGTH
  1660. if (!strcmp(cmd, "get_max_blade_length") && arg) {
  1661. STDOUT.println(GetMaxBladeLength(atoi(arg)));
  1662. return true;
  1663. }
  1664. if (!strcmp(cmd, "get_blade_length") && arg) {
  1665. STDOUT.println(GetBladeLength(atoi(arg)));
  1666. return true;
  1667. }
  1668. if (!strcmp(cmd, "set_blade_length") && arg) {
  1669. SetBladeLength(atoi(arg), atoi(SkipWord(arg)));
  1670. SaveState(current_preset_.preset_num);
  1671. // Reload preset to make the change take effect.
  1672. SetPreset(current_preset_.preset_num, false);
  1673. return true;
  1674. }
  1675. #endif
  1676.  
  1677. #ifdef DYNAMIC_BLADE_DIMMING
  1678. if (!strcmp(cmd, "get_blade_dimming")) {
  1679. STDOUT.println(SaberBase::GetCurrentDimming());
  1680. return true;
  1681. }
  1682. if (!strcmp(cmd, "set_blade_dimming") && arg) {
  1683. SaberBase::SetDimming(atoi(arg));
  1684. return true;
  1685. }
  1686. #endif
  1687.  
  1688. #ifdef DYNAMIC_CLASH_THRESHOLD
  1689. if (!strcmp(cmd, "get_clash_threshold")) {
  1690. STDOUT.println(GetCurrentClashThreshold());
  1691. return true;
  1692. }
  1693. if (!strcmp(cmd, "set_clash_threshold") && arg) {
  1694. SetClashThreshold(parsefloat(arg));
  1695. return true;
  1696. }
  1697. #endif
  1698.  
  1699. if (!strcmp(cmd, "get_preset")) {
  1700. STDOUT.println(current_preset_.preset_num);
  1701. return true;
  1702. }
  1703. if (!strcmp(cmd, "get_volume")) {
  1704. #ifdef ENABLE_AUDIO
  1705. STDOUT.println(dynamic_mixer.get_volume());
  1706. #else
  1707. STDOUT.println(0);
  1708. #endif
  1709. return true;
  1710. }
  1711. if (!strcmp(cmd, "set_volume") && arg) {
  1712. #ifdef ENABLE_AUDIO
  1713. int32_t volume = strtol(arg, NULL, 0);
  1714. if (volume >= 0 && volume <= 3000) {
  1715. dynamic_mixer.set_volume(volume);
  1716. PollSaveColorChange();
  1717. }
  1718. #endif
  1719. return true;
  1720. }
  1721. if (!strcmp(cmd, "mute")) {
  1722. SetMute(true);
  1723. return true;
  1724. }
  1725. if (!strcmp(cmd, "unmute")) {
  1726. SetMute(false);
  1727. return true;
  1728. }
  1729. if (!strcmp(cmd, "toggle_mute")) {
  1730. if (!SetMute(true)) SetMute(false);
  1731. return true;
  1732. }
  1733.  
  1734. if (!strcmp(cmd, "set_preset") && arg) {
  1735. int preset = strtol(arg, NULL, 0);
  1736. SetPreset(preset, true);
  1737. return true;
  1738. }
  1739.  
  1740. if (!strcmp(cmd, "change_preset") && arg) {
  1741. int preset = strtol(arg, NULL, 0);
  1742. if (preset != current_preset_.preset_num) {
  1743. SetPreset(preset, true);
  1744. }
  1745. return true;
  1746. }
  1747.  
  1748. #ifndef DISABLE_COLOR_CHANGE
  1749. if (arg && (!strcmp(cmd, "var") || !strcmp(cmd, "variation"))) {
  1750. size_t variation = strtol(arg, NULL, 0);
  1751. SaberBase::SetVariation(variation);
  1752. return true;
  1753. }
  1754. if (!strcmp(cmd, "ccmode")) {
  1755. ToggleColorChangeMode();
  1756. return true;
  1757. }
  1758. #endif
  1759.  
  1760. #ifdef ENABLE_SD
  1761. if (!strcmp(cmd, "list_tracks")) {
  1762. // Tracks are must be in: tracks/*.wav or */tracks/*.wav
  1763. LOCK_SD(true);
  1764. ListTracks("tracks");
  1765. for (LSFS::Iterator iter("/"); iter; ++iter) {
  1766. if (iter.isdir()) {
  1767. PathHelper path(iter.name(), "tracks");
  1768. ListTracks(path);
  1769. }
  1770. }
  1771. LOCK_SD(false);
  1772. return true;
  1773. }
  1774.  
  1775. if (!strcmp(cmd, "list_fonts")) {
  1776. LOCK_SD(true);
  1777. for (LSFS::Iterator iter("/"); iter; ++iter) {
  1778. if (iter.name()[0] == '.') continue;
  1779. if (!strcmp(iter.name(), "common")) continue;
  1780. if (!iter.isdir()) continue;
  1781. bool isfont = false;
  1782. for (LSFS::Iterator i2(iter); i2 && !isfont; ++i2) {
  1783. if (i2.isdir()) {
  1784. if (!strcasecmp("hum", i2.name())) isfont = true;
  1785. if (!strcasecmp("alt000", i2.name())) isfont = true;
  1786. } else {
  1787. const char* tmp = i2.name();
  1788. if (!startswith("hum", tmp)) continue;
  1789. tmp += 3;
  1790. if (startswith("m", tmp)) tmp++;
  1791. while (*tmp >= '0' && *tmp <= '9') tmp++;
  1792. if (!strcasecmp(".wav", tmp)) isfont = true;
  1793. }
  1794. }
  1795. if (isfont) {
  1796. STDOUT.println(iter.name());
  1797. }
  1798. }
  1799. LOCK_SD(false);
  1800. return true;
  1801. }
  1802. #endif
  1803. return false;
  1804. }
  1805.  
  1806. virtual bool Event(enum BUTTON button, EVENT event) {
  1807. PrintEvent(button, event);
  1808.  
  1809. switch (event) {
  1810. case EVENT_RELEASED:
  1811. clash_pending_ = false;
  1812. case EVENT_PRESSED:
  1813. IgnoreClash(50); // ignore clashes to prevent buttons from causing clashes
  1814. default:
  1815. break;
  1816. }
  1817.  
  1818. if (Event2(button, event, current_modifiers | (IsOn() ? MODE_ON : MODE_OFF))) {
  1819. current_modifiers = 0;
  1820. return true;
  1821. }
  1822. if (Event2(button, event, MODE_ANY_BUTTON | (IsOn() ? MODE_ON : MODE_OFF))) {
  1823. // Not matching modifiers, so no need to clear them.
  1824. current_modifiers &= ~button;
  1825. return true;
  1826. }
  1827. return false;
  1828. }
  1829.  
  1830. virtual bool Event2(enum BUTTON button, EVENT event, uint32_t modifiers) = 0;
  1831.  
  1832. private:
  1833. bool CommonIgnition() {
  1834. if (IsOn()) return false;
  1835. if (current_style() && current_style()->NoOnOff())
  1836. return false;
  1837. activated_ = millis();
  1838. STDOUT.println("Ignition.");
  1839. MountSDCard();
  1840. EnableAmplifier();
  1841. SaberBase::RequestMotion();
  1842.  
  1843. // Avoid clashes a little bit while turning on.
  1844. // It might be a "clicky" power button...
  1845. IgnoreClash(300);
  1846. return true;
  1847. }
  1848.  
  1849. protected:
  1850. CurrentPreset current_preset_;
  1851. LoopCounter accel_loop_counter_;
  1852. };
  1853.  
  1854. #endif
  1855.  
  1856. // 3 sound lib
  1857. // proffieos.ino needs prop.FindBlade(true);
  1858.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement