Advertisement
NoSloppy

Revo prop_base_edited_for_batt_toers

Sep 16th, 2021 (edited)
117
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 42.69 KB | None | 0 0
  1. #ifndef PROPS_PROP_BASE_H
  2. #define PROPS_PROP_BASE_H
  3.  
  4. class SaveGlobalStateFile : public ConfigFile {
  5. public:
  6. void SetVariable(const char* variable, float v) override {
  7. CONFIG_VARIABLE(volume, -1);
  8. }
  9. int volume;
  10. };
  11.  
  12. class SavePresetStateFile : public ConfigFile {
  13. public:
  14. void SetVariable(const char* variable, float v) override {
  15. CONFIG_VARIABLE(preset, 0);
  16. }
  17. int preset;
  18. };
  19.  
  20.  
  21. // Base class for props.
  22. class PropBase : CommandParser, Looper, protected SaberBase {
  23. public:
  24. PropBase() : CommandParser() {}
  25. BladeStyle* current_style(){
  26. #if NUM_BLADES == 0
  27. return nullptr;
  28. #else
  29. return current_config->blade1->current_style();
  30. #endif
  31. }
  32.  
  33. bool NeedsPower() {
  34. if (SaberBase::IsOn()) return true;
  35. if (current_style() && current_style()->NoOnOff())
  36. return true;
  37. return false;
  38. }
  39.  
  40. int32_t muted_volume_ = 0;
  41. bool SetMute(bool muted) {
  42. #ifdef ENABLE_AUDIO
  43. if (muted) {
  44. if (dynamic_mixer.get_volume()) {
  45. muted_volume_ = dynamic_mixer.get_volume();
  46. dynamic_mixer.set_volume(0);
  47. return true;
  48. }
  49. } else {
  50. if (muted_volume_) {
  51. dynamic_mixer.set_volume(muted_volume_);
  52. muted_volume_ = 0;
  53. return true;
  54. }
  55. }
  56. #endif
  57. return false;
  58. }
  59.  
  60. bool unmute_on_deactivation_ = false;
  61. uint32_t activated_ = 0;
  62. uint32_t last_clash_ = 0;
  63. uint32_t clash_timeout_ = 100;
  64. bool clash_pending_ = false;
  65. bool pending_clash_is_stab_ = false;
  66.  
  67. bool on_pending_ = false;
  68. uint32_t on_pending_base_;
  69. uint32_t on_pending_delay_;
  70.  
  71. virtual bool IsOn() {
  72. return SaberBase::IsOn() || on_pending_;
  73. }
  74.  
  75. virtual void On() {
  76. if (IsOn()) return;
  77. if (current_style() && current_style()->NoOnOff())
  78. return;
  79. activated_ = millis();
  80. STDOUT.println("Ignition.");
  81. MountSDCard();
  82. EnableAmplifier();
  83. SaberBase::RequestMotion();
  84.  
  85. // Avoid clashes a little bit while turning on.
  86. // It might be a "clicky" power button...
  87. IgnoreClash(300);
  88.  
  89. float preon_time = 0.0;
  90. SaberBase::DoPreOn(&preon_time);
  91. if (preon_time > 0.0) {
  92. on_pending_ = true;
  93. on_pending_base_ = millis();
  94. on_pending_delay_ = preon_time * 1000;
  95. } else {
  96. SaberBase::TurnOn();
  97. }
  98. }
  99.  
  100. virtual void Off(OffType off_type = OFF_NORMAL) {
  101. if (on_pending_) {
  102. // Or is it better to wait until we turn on, and then turn off?
  103. on_pending_ = false;
  104. return;
  105. }
  106. if (!SaberBase::IsOn()) return;
  107. if (SaberBase::Lockup()) {
  108. SaberBase::DoEndLockup();
  109. SaberBase::SetLockup(SaberBase::LOCKUP_NONE);
  110. }
  111. #ifndef DISABLE_COLOR_CHANGE
  112. if (SaberBase::GetColorChangeMode() != SaberBase::COLOR_CHANGE_MODE_NONE) {
  113. ToggleColorChangeMode();
  114. }
  115. #endif
  116. SaberBase::TurnOff(off_type);
  117. if (unmute_on_deactivation_) {
  118. unmute_on_deactivation_ = false;
  119. #ifdef ENABLE_AUDIO
  120. // We may also need to stop any thing else that generates noise..
  121. for (size_t i = 0; i < NELEM(wav_players); i++) {
  122. wav_players[i].Stop();
  123. }
  124. #endif
  125. SetMute(false);
  126. }
  127. }
  128.  
  129. void IgnoreClash(size_t ms) {
  130. if (clash_pending_) return;
  131. uint32_t now = millis();
  132. uint32_t time_since_last_clash = now - last_clash_;
  133. if (time_since_last_clash < clash_timeout_) {
  134. ms = std::max<size_t>(ms, clash_timeout_ - time_since_last_clash);
  135. }
  136. last_clash_ = now;
  137. clash_timeout_ = ms;
  138. }
  139.  
  140. virtual void Clash2(bool stab) {
  141. if (Event(BUTTON_NONE, stab ? EVENT_STAB : EVENT_CLASH)) {
  142. IgnoreClash(400);
  143. } else {
  144. IgnoreClash(100);
  145. if (SaberBase::IsOn()) {
  146. if (stab) {
  147. SaberBase::DoStab();
  148. } else {
  149. SaberBase::DoClash();
  150. }
  151. }
  152. }
  153. }
  154.  
  155. virtual void Clash(bool stab) {
  156. // No clashes in lockup mode.
  157. if (SaberBase::Lockup()) return;
  158. // TODO: Pick clash randomly and/or based on strength of clash.
  159. uint32_t t = millis();
  160. if (t - last_clash_ < clash_timeout_) {
  161. last_clash_ = t; // Vibration cancellation
  162. return;
  163. }
  164. if (current_modifiers & ~MODE_ON) {
  165. // Some button is pressed, that means that we need to delay the clash a little
  166. // to see if was caused by a button *release*.
  167. last_clash_ = millis();
  168. clash_timeout_ = 3;
  169. clash_pending_ = true;
  170. pending_clash_is_stab_ = stab;
  171. return;
  172. }
  173. Clash2(stab);
  174. }
  175.  
  176. bool chdir(const char* dir) {
  177. if (strlen(dir) > 1 && dir[strlen(dir)-1] == '/') {
  178. STDOUT.println("Directory must not end with slash.");
  179. return false;
  180. }
  181. #ifdef ENABLE_AUDIO
  182. smooth_swing_v2.Deactivate();
  183. looped_swing_wrapper.Deactivate();
  184. hybrid_font.Deactivate();
  185.  
  186. // Stop all sound!
  187. // TODO: Move scanning to wav-playing interrupt level so we can
  188. // interleave things without worry about memory corruption.
  189. for (size_t i = 0; i < NELEM(wav_players); i++) {
  190. wav_players[i].Stop();
  191. }
  192. #endif
  193.  
  194. char *b = current_directory;
  195. for (const char *a = dir; *a; a++) {
  196. // Skip trailing slash
  197. if (*a == '/' && (a[1] == 0 || a[1] == ';'))
  198. continue;
  199. if (*a == ';') {
  200. *(b++) = 0;
  201. continue;
  202. }
  203. *(b++) = *a;
  204. }
  205. // Two zeroes at end!
  206. *(b++) = 0;
  207. *(b++) = 0;
  208.  
  209. #ifdef ENABLE_AUDIO
  210. Effect::ScanCurrentDirectory();
  211. SaberBase* font = NULL;
  212. hybrid_font.Activate();
  213. font = &hybrid_font;
  214. if (font) {
  215. smooth_swing_config.ReadInCurrentDir("smoothsw.ini");
  216. if (SFX_lswing) {
  217. smooth_swing_cfx_config.ReadInCurrentDir("font_config.txt");
  218. // map CFX values to Proffie (sourced from font_config.txt in font folder)
  219. smooth_swing_config.SwingSensitivity = smooth_swing_cfx_config.smooth_sens;
  220. smooth_swing_config.MaximumHumDucking = smooth_swing_cfx_config.smooth_dampen;
  221. smooth_swing_config.SwingSharpness = smooth_swing_cfx_config.smooth_sharp;
  222. smooth_swing_config.SwingStrengthThreshold = smooth_swing_cfx_config.smooth_gate;
  223. smooth_swing_config.Transition1Degrees = smooth_swing_cfx_config.smooth_width1;
  224. smooth_swing_config.Transition2Degrees = smooth_swing_cfx_config.smooth_width2;
  225. smooth_swing_config.MaxSwingVolume = smooth_swing_cfx_config.smooth_gain * 3 / 100;
  226. smooth_swing_config.AccentSwingSpeedThreshold = smooth_swing_cfx_config.hswing;
  227. smooth_swing_config.Version = 2;
  228. } else if (!SFX_swingl) {
  229. smooth_swing_config.Version = 0;
  230. }
  231. switch (smooth_swing_config.Version) {
  232. case 1:
  233. looped_swing_wrapper.Activate(font);
  234. break;
  235. case 2:
  236. smooth_swing_v2.Activate(font);
  237. break;
  238. }
  239. }
  240. // EnableBooster();
  241. #endif
  242. return false;
  243. }
  244.  
  245. void SaveVolumeIfNeeded() {
  246. #ifdef SAVE_VOLUME
  247. if (dynamic_mixer.get_volume() != saved_global_state.volume) {
  248. SaveGlobalState();
  249. }
  250. #endif
  251. }
  252.  
  253. void SaveColorChangeIfNeeded() {
  254. #ifdef SAVE_COLOR_CHANGE
  255. if (current_preset_.variation != SaberBase::GetCurrentVariation()) {
  256. current_preset_.variation = SaberBase::GetCurrentVariation();
  257. current_preset_.Save();
  258. }
  259. #endif
  260. }
  261.  
  262. void PollSaveColorChange() {
  263. #ifdef ENABLE_AUDIO
  264. if (AmplifierIsActive()) return; // Do it later
  265. #endif
  266. SaveColorChangeIfNeeded();
  267. SaveVolumeIfNeeded();
  268. }
  269.  
  270. // Select preset (font/style)
  271. virtual void SetPreset(int preset_num, bool announce) {
  272. TRACE("start");
  273. #ifdef IDLE_OFF_TIME
  274. last_on_time_ = millis();
  275. #endif
  276. bool on = SaberBase::IsOn();
  277. if (on) Off();
  278. SaveColorChangeIfNeeded();
  279. // First free all styles, then allocate new ones to avoid memory
  280. // fragmentation.
  281. #define UNSET_BLADE_STYLE(N) \
  282. delete current_config->blade##N->UnSetStyle();
  283. ONCEPERBLADE(UNSET_BLADE_STYLE)
  284. current_preset_.SetPreset(preset_num);
  285. if (announce) {
  286. if (current_preset_.name.get()) {
  287. SaberBase::DoMessage(current_preset_.name.get());
  288. } else {
  289. char message[64];
  290. strcpy(message, "Preset: ");
  291. itoa(current_preset_.preset_num + 1,
  292. message + strlen(message), 10);
  293. strcat(message, "\n");
  294. strncat(message + strlen(message),
  295. current_preset_.font.get(), sizeof(message) - strlen(message));
  296. message[sizeof(message) - 1] = 0;
  297. SaberBase::DoMessage(message);
  298. }
  299. }
  300.  
  301. #define SET_BLADE_STYLE(N) \
  302. current_config->blade##N->SetStyle(style_parser.Parse(current_preset_.current_style##N.get()));
  303. ONCEPERBLADE(SET_BLADE_STYLE)
  304. chdir(current_preset_.font.get());
  305.  
  306. #ifdef SAVE_COLOR_CHANGE
  307. SaberBase::SetVariation(current_preset_.variation);
  308. #else
  309. SaberBase::SetVariation(0);
  310. #endif
  311.  
  312. if (on) On();
  313. if (announce) SaberBase::DoNewFont();
  314. TRACE("end");
  315. }
  316.  
  317. // Go to the next Preset.
  318. virtual void next_preset() {
  319. #ifdef SAVE_PRESET
  320. SaveState(current_preset_.preset_num + 1);
  321. #endif
  322. SetPreset(current_preset_.preset_num + 1, true);
  323. }
  324.  
  325. // Go to the previous Preset.
  326. virtual void previous_preset() {
  327. #ifdef SAVE_PRESET
  328. SaveState(current_preset_.preset_num - 1);
  329. #endif
  330. SetPreset(current_preset_.preset_num - 1, true);
  331. }
  332.  
  333. // Rotates presets backwards and saves.
  334. virtual void rotate_presets() {
  335. #ifdef IDLE_OFF_TIME
  336. last_on_time_ = millis();
  337. #endif
  338. #ifdef ENABLE_AUDIO
  339. beeper.Beep(0.05, 2000.0);
  340. #endif
  341. LOCK_SD(true);
  342. current_preset_.Load(-1); // load last preset
  343. current_preset_.SaveAt(0); // save in first position, shifting all other presets down
  344. LOCK_SD(false);
  345. SetPreset(0, true);
  346. }
  347.  
  348. #ifdef BLADE_DETECT_PIN
  349. bool blade_detected_ = false;
  350. #endif
  351.  
  352. // Measure and return the blade identifier resistor.
  353. float id() {
  354. BLADE_ID_CLASS_INTERNAL blade_id;
  355. float ret = blade_id.id();
  356. STDOUT << "ID: " << ret << "\n";
  357. #ifdef SPEAK_BLADE_ID
  358. talkie.Say(spI);
  359. talkie.Say(spD);
  360. talkie.SayNumber((int)ret);
  361. #endif
  362. #ifdef BLADE_DETECT_PIN
  363. if (!blade_detected_) {
  364. STDOUT << "NO ";
  365. ret += NO_BLADE;
  366. }
  367. STDOUT << "Blade Detected\n";
  368. #endif
  369. return ret;
  370. }
  371.  
  372. // Called from setup to identify the blade and select the right
  373. // Blade driver, style and sound font.
  374. void FindBlade() {
  375. size_t best_config = 0;
  376. if (NELEM(blades) > 1) {
  377. float resistor = id();
  378.  
  379. float best_err = 100000000.0;
  380. for (size_t i = 0; i < NELEM(blades); i++) {
  381. float err = fabsf(resistor - blades[i].ohm);
  382. if (err < best_err) {
  383. best_config = i;
  384. best_err = err;
  385. }
  386. }
  387. }
  388. STDOUT.print("blade= ");
  389. STDOUT.println(best_config);
  390. current_config = blades + best_config;
  391.  
  392. #define ACTIVATE(N) do { \
  393. if (!current_config->blade##N) goto bad_blade; \
  394. current_config->blade##N->Activate(); \
  395. } while(0);
  396.  
  397. ONCEPERBLADE(ACTIVATE);
  398. RestoreGlobalState();
  399. #ifdef SAVE_PRESET
  400. ResumePreset();
  401. #else
  402. SetPreset(0, false);
  403. #endif
  404. return;
  405.  
  406. #if NUM_BLADES != 0
  407.  
  408. bad_blade:
  409. STDOUT.println("BAD BLADE");
  410. #ifdef ENABLE_AUDIO
  411. talkie.Say(talkie_error_in_15, 15);
  412. talkie.Say(talkie_blade_array_15, 15);
  413. #endif
  414.  
  415. #endif
  416. }
  417.  
  418. void ResumePreset() {
  419. SavePresetStateFile savestate;
  420. savestate.ReadINIFromSaveDir("curstate");
  421. SetPreset(savestate.preset, false);
  422. }
  423.  
  424. void WriteState(const char *filename, int preset) {
  425. PathHelper fn(GetSaveDir(), filename);
  426. LOCK_SD(true);
  427. FileReader out;
  428. LSFS::Remove(fn);
  429. out.Create(fn);
  430. out.write_key_value("installed", install_time);
  431. out.write_key_value("preset", preset);
  432. out.write_key_value("end", "1");
  433. out.Close();
  434. LOCK_SD(false);
  435. }
  436.  
  437. void SaveState(int preset) {
  438. STDOUT.println("Saving Current Preset");
  439. WriteState("curstate.tmp", preset);
  440. WriteState("curstate.ini", preset);
  441. }
  442.  
  443. SaveGlobalStateFile saved_global_state;
  444. void RestoreGlobalState() {
  445. #ifdef SAVE_VOLUME
  446. saved_global_state.ReadINIFromDir(NULL, "global");
  447. if (saved_global_state.volume >= 0) {
  448. dynamic_mixer.set_volume(clampi32(saved_global_state.volume, 0, VOLUME));
  449. }
  450. #endif
  451. }
  452.  
  453. void WriteGlobalState(const char* filename) {
  454. LOCK_SD(true);
  455. FileReader out;
  456. LSFS::Remove(filename);
  457. out.Create(filename);
  458. out.write_key_value("installed", install_time);
  459. #ifdef ENABLE_AUDIO
  460. out.write_key_value("volume", muted_volume_ ? muted_volume_ : dynamic_mixer.get_volume());
  461. #endif
  462. out.write_key_value("end", "1");
  463. out.Close();
  464. LOCK_SD(false);
  465. }
  466.  
  467. void SaveGlobalState() {
  468. #ifdef SAVE_VOLUME
  469. STDOUT.println("Saving Global State");
  470. WriteGlobalState("global.tmp");
  471. WriteGlobalState("global.ini");
  472. saved_global_state.volume = dynamic_mixer.get_volume();
  473. #endif
  474. }
  475.  
  476. void FindBladeAgain() {
  477. if (!current_config) {
  478. // FindBlade() hasn't been called yet - ignore this.
  479. return;
  480. }
  481. // Reverse everything that FindBlade does.
  482.  
  483. // First free all styles, then allocate new ones to avoid memory
  484. // fragmentation.
  485. ONCEPERBLADE(UNSET_BLADE_STYLE)
  486.  
  487. #define DEACTIVATE(N) do { \
  488. if (current_config->blade##N) \
  489. current_config->blade##N->Deactivate(); \
  490. } while(0);
  491.  
  492. ONCEPERBLADE(DEACTIVATE);
  493.  
  494. FindBlade();
  495. }
  496.  
  497. void SB_Message(const char* text) override {
  498. STDOUT.print("DISPLAY: ");
  499. STDOUT.println(text);
  500. }
  501.  
  502. float peak = 0.0;
  503. Vec3 at_peak;
  504. virtual void DoAccel(const Vec3& accel, bool clear) {
  505. SaberBase::DoAccel(accel, clear);
  506. accel_loop_counter_.Update();
  507. if (clear) accel_ = accel;
  508. Vec3 diff = (accel - fusor.down());
  509. if (clear) diff = Vec3(0,0,0);
  510. float v = diff.len();
  511. // If we're spinning the saber, require a stronger acceleration
  512. // to activate the clash.
  513. if (v > CLASH_THRESHOLD_G + fusor.gyro().len() / 200.0) {
  514. if ( (accel_ - fusor.down()).len2() > (accel - fusor.down()).len2() ) {
  515. diff = -diff;
  516. }
  517. bool stab = diff.x < - 2.0 * sqrtf(diff.y * diff.y + diff.z * diff.z) &&
  518. #if 0
  519. Vec3 speed = fusor.speed();
  520. // Speed checks simply don't work yet
  521. speed.y * speed.y + speed.z * speed.z < 5.0 && // TODO: Make this tighter
  522. speed.x > 0.1 &&
  523. #endif
  524. fusor.swing_speed() < 150;
  525.  
  526. #if 1
  527. STDOUT << "ACCEL: " << accel
  528. << " diff=" << diff
  529. << " v=" << v
  530. << " fgl=" << (fusor.gyro().len() / 200.0)
  531. << " accel_=" << accel_
  532. << " clear=" << clear
  533. << " millis=" << millis()
  534. << " swing_speed=" << fusor.swing_speed()
  535. << " mss=" << fusor.mss()
  536. << " stab=" << stab
  537. << "\n";
  538. #endif
  539. // Needs de-bouncing
  540. Clash(stab);
  541. }
  542. if (v > peak) {
  543. peak = v;
  544. at_peak = accel_ - accel;
  545. }
  546. accel_ = accel;
  547. if (monitor.ShouldPrint(Monitoring::MonitorClash)) {
  548. STDOUT.print("ACCEL: ");
  549. STDOUT.print(accel.x);
  550. STDOUT.print(", ");
  551. STDOUT.print(accel.y);
  552. STDOUT.print(", ");
  553. STDOUT.print(accel.z);
  554. STDOUT.print(" peak ");
  555. STDOUT.print(at_peak.x);
  556. STDOUT.print(", ");
  557. STDOUT.print(at_peak.y);
  558. STDOUT.print(", ");
  559. STDOUT.print(at_peak.z);
  560. STDOUT.print(" (");
  561. STDOUT.print(peak);
  562. STDOUT.println(")");
  563. peak = 0.0;
  564. }
  565. }
  566.  
  567. virtual void DoMotion(const Vec3& motion, bool clear) {
  568. SaberBase::DoMotion(motion, clear);
  569. }
  570.  
  571. void SB_Top(uint64_t total_cycles) override {
  572. STDOUT.print("Acceleration measurements per second: ");
  573. accel_loop_counter_.Print();
  574. STDOUT.println("");
  575. }
  576.  
  577. enum StrokeType {
  578. TWIST_CLOSE,
  579. TWIST_LEFT,
  580. TWIST_RIGHT,
  581.  
  582. SHAKE_CLOSE,
  583. SHAKE_FWD,
  584. SHAKE_REW
  585. };
  586. struct Stroke {
  587. StrokeType type;
  588. uint32_t start_millis;
  589. uint32_t end_millis;
  590. uint32_t length() const { return end_millis - start_millis; }
  591. };
  592.  
  593. Stroke strokes[5];
  594.  
  595. void MonitorStrokes() {
  596. if (monitor.IsMonitoring(Monitoring::MonitorStrokes)) {
  597. STDOUT.print("Stroke: ");
  598. switch (strokes[NELEM(strokes)-1].type) {
  599. case TWIST_LEFT:
  600. STDOUT.print("TwistLeft");
  601. break;
  602. case TWIST_RIGHT:
  603. STDOUT.print("TwistRight");
  604. break;
  605. case SHAKE_FWD:
  606. STDOUT.print("Thrust");
  607. break;
  608. case SHAKE_REW:
  609. STDOUT.print("Yank");
  610. break;
  611. default: break;
  612. }
  613. STDOUT << " len = " << strokes[NELEM(strokes)-1].length();
  614. uint32_t separation =
  615. strokes[NELEM(strokes)-1].start_millis -
  616. strokes[NELEM(strokes)-2].end_millis;
  617. STDOUT << " separation=" << separation
  618. << " mss=" << fusor.mss()
  619. << " swspd=" << fusor.swing_speed()
  620. << "\n";
  621. }
  622. }
  623.  
  624. StrokeType GetStrokeGroup(StrokeType a) {
  625. switch (a) {
  626. case TWIST_CLOSE:
  627. case TWIST_LEFT:
  628. case TWIST_RIGHT:
  629. return TWIST_CLOSE;
  630. case SHAKE_CLOSE:
  631. case SHAKE_FWD:
  632. case SHAKE_REW:
  633. break;
  634. }
  635. return SHAKE_CLOSE;
  636. }
  637.  
  638. bool ShouldClose(StrokeType a, StrokeType b) {
  639. // Don't close if it's the same exact stroke
  640. if (a == b) return false;
  641. // Different stroke in same stroke group -> close
  642. if (GetStrokeGroup(a) == GetStrokeGroup(b)) return true;
  643. // New stroke in different group -> close
  644. if (GetStrokeGroup(b) != b) return true;
  645. return false;
  646. }
  647.  
  648. bool DoGesture(StrokeType gesture) {
  649. if (gesture == strokes[NELEM(strokes)-1].type) {
  650. if (strokes[NELEM(strokes)-1].end_millis == 0) {
  651. // Stroke not done, wait.
  652. return false;
  653. }
  654. if (millis() - strokes[NELEM(strokes)-1].end_millis < 50) {
  655. // Stroke continues
  656. strokes[NELEM(strokes)-1].end_millis = millis();
  657. return false;
  658. }
  659. }
  660. if (strokes[NELEM(strokes) - 1].end_millis == 0 &&
  661. GetStrokeGroup(gesture) == GetStrokeGroup(strokes[NELEM(strokes) - 1].type)) {
  662. strokes[NELEM(strokes) - 1].end_millis = millis();
  663. MonitorStrokes();
  664. return true;
  665. }
  666. // Exit here if it's a *_CLOSE stroke.
  667. if (GetStrokeGroup(gesture) == gesture) return false;
  668. // If last stroke is very short, just write over it.
  669. if (strokes[NELEM(strokes)-1].end_millis -
  670. strokes[NELEM(strokes)-1].start_millis > 10) {
  671. for (size_t i = 0; i < NELEM(strokes) - 1; i++) {
  672. strokes[i] = strokes[i+1];
  673. }
  674. }
  675. strokes[NELEM(strokes)-1].type = gesture;
  676. strokes[NELEM(strokes)-1].start_millis = millis();
  677. strokes[NELEM(strokes)-1].end_millis = 0;
  678. return false;
  679. }
  680.  
  681. // The prop should call this from Loop() if it wants to detect twists.
  682. void DetectTwist() {
  683. Vec3 gyro = fusor.gyro();
  684. bool process = false;
  685. if (fabsf(gyro.x) > 200.0 &&
  686. fabsf(gyro.x) > 3.0f * abs(gyro.y) &&
  687. fabsf(gyro.x) > 3.0f * abs(gyro.z)) {
  688. process = DoGesture(gyro.x > 0 ? TWIST_LEFT : TWIST_RIGHT);
  689. } else {
  690. process = DoGesture(TWIST_CLOSE);
  691. }
  692. if (process) {
  693. if ((strokes[NELEM(strokes)-1].type == TWIST_LEFT &&
  694. strokes[NELEM(strokes)-2].type == TWIST_RIGHT) ||
  695. (strokes[NELEM(strokes)-1].type == TWIST_RIGHT &&
  696. strokes[NELEM(strokes)-2].type == TWIST_LEFT)) {
  697. if (strokes[NELEM(strokes) -1].length() > 90UL &&
  698. strokes[NELEM(strokes) -1].length() < 300UL &&
  699. strokes[NELEM(strokes) -2].length() > 90UL &&
  700. strokes[NELEM(strokes) -2].length() < 300UL) {
  701. uint32_t separation =
  702. strokes[NELEM(strokes)-1].start_millis -
  703. strokes[NELEM(strokes)-2].end_millis;
  704. if (separation < 200UL) {
  705. STDOUT.println("TWIST");
  706. // We have a twisting gesture.
  707. Event(BUTTON_NONE, EVENT_TWIST);
  708. }
  709. }
  710. }
  711. }
  712. }
  713.  
  714. // The prop should call this from Loop() if it wants to detect shakes.
  715. void DetectShake() {
  716. Vec3 mss = fusor.mss();
  717. bool process = false;
  718. if (mss.y * mss.y + mss.z * mss.z < 16.0 &&
  719. (mss.x > 7 || mss.x < -6) &&
  720. fusor.swing_speed() < 150) {
  721. process = DoGesture(mss.x > 0 ? SHAKE_FWD : SHAKE_REW);
  722. } else {
  723. process = DoGesture(SHAKE_CLOSE);
  724. }
  725. if (process) {
  726. int i;
  727. for (i = 0; i < 5; i++) {
  728. if (strokes[NELEM(strokes)-1-i].type !=
  729. ((i & 1) ? SHAKE_REW : SHAKE_FWD)) break;
  730. if (i) {
  731. uint32_t separation =
  732. strokes[NELEM(strokes)-i].start_millis -
  733. strokes[NELEM(strokes)-1-i].end_millis;
  734. if (separation > 250) break;
  735. }
  736. }
  737. if (i == 5) {
  738. strokes[NELEM(strokes)-1].type = SHAKE_CLOSE;
  739. Event(BUTTON_NONE, EVENT_SHAKE);
  740. }
  741. }
  742. }
  743.  
  744. bool swinging_ = false;
  745. // The prop should call this from Loop() if it wants to detect swings as an event.
  746. void DetectSwing() {
  747. if (!swinging_ && fusor.swing_speed() > 250) {
  748. swinging_ = true;
  749. Event(BUTTON_NONE, EVENT_SWING);
  750. }
  751. if (swinging_ && fusor.swing_speed() < 100) {
  752. swinging_ = false;
  753. }
  754. }
  755.  
  756. void SB_Motion(const Vec3& gyro, bool clear) override {
  757. if (monitor.ShouldPrint(Monitoring::MonitorGyro)) {
  758. // Got gyro data
  759. STDOUT.print("GYRO: ");
  760. STDOUT.print(gyro.x);
  761. STDOUT.print(", ");
  762. STDOUT.print(gyro.y);
  763. STDOUT.print(", ");
  764. STDOUT.println(gyro.z);
  765. }
  766. }
  767.  
  768. Vec3 accel_;
  769.  
  770. void StartOrStopTrack() {
  771. #ifdef ENABLE_AUDIO
  772. if (track_player_) {
  773. track_player_->Stop();
  774. track_player_.Free();
  775. } else {
  776. MountSDCard();
  777. EnableAmplifier();
  778. track_player_ = GetFreeWavPlayer();
  779. if (track_player_) {
  780. track_player_->Play(current_preset_.track.get());
  781. } else {
  782. STDOUT.println("No available WAV players.");
  783. }
  784. }
  785. #else
  786. STDOUT.println("Audio disabled.");
  787. #endif
  788. }
  789.  
  790. void ListTracks(const char* dir) {
  791. if (!LSFS::Exists(dir)) return;
  792. for (LSFS::Iterator i2(dir); i2; ++i2) {
  793. if (endswith(".wav", i2.name()) && i2.size() > 200000) {
  794. STDOUT << dir << "/" << i2.name() << "\n";
  795. }
  796. }
  797. }
  798.  
  799. virtual void LowBatteryOff() {
  800. if (SaberBase::IsOn()) {
  801. STDOUT.print("Battery low, turning off. Battery voltage: ");
  802. STDOUT.println(battery_monitor.battery());
  803. Off();
  804. }
  805. }
  806.  
  807. virtual void CheckLowBattery() {
  808. if (battery_monitor.low()) {
  809. if (current_style() && !current_style()->Charging()) {
  810. LowBatteryOff();
  811. if (millis() - last_beep_ > 15000) { // (was 5000)
  812. STDOUT << "Low battery: " << battery_monitor.battery() << " volts\n";
  813. SaberBase::DoLowBatt();
  814. last_beep_ = millis();
  815. }
  816. }
  817. }
  818. }
  819.  
  820. uint32_t last_beep_;
  821. float current_tick_angle_ = 0.0;
  822. bool low_batt_a_ = false; // BC REVO LOW BATT TIERS
  823. bool low_batt_b_ = false; // BC REVO LOW BATT TIERS
  824. bool low_batt_c_ = false; // BC REVO LOW BATT TIERS
  825. bool low_batt_d_ = false; // BC REVO LOW BATT TIERS
  826.  
  827. void Loop() override {
  828. if (on_pending_ && millis() - on_pending_base_ >= on_pending_delay_) {
  829. on_pending_ = false;
  830. SaberBase::TurnOn();
  831. }
  832.  
  833. if (clash_pending_ && millis() - last_clash_ >= clash_timeout_) {
  834. clash_pending_ = false;
  835. Clash2(pending_clash_is_stab_);
  836. }
  837. CheckLowBattery();
  838. //-------------------------------------------------------------------------// BC REVO LOW BATT TIERS
  839. #ifdef LOW_BATT_WARNINGS_TIERED
  840. if (battery_monitor.battery() < 3.25 && (!low_batt_a_)){
  841. ::CommandParser::DoParse("play", "lowbatt1.wav");
  842. low_batt_a_ = true;
  843. }
  844. if (battery_monitor.battery() < 3.20 && (!low_batt_b_)){
  845. ::CommandParser::DoParse("play", "lowbatt2.wav");
  846. low_batt_b_ = true;
  847. }
  848. if (battery_monitor.battery() < 3.15 && (!low_batt_c_)){
  849. ::CommandParser::DoParse("play", "lowbatt3.wav");
  850. low_batt_c_ = true;
  851. }
  852. if (battery_monitor.battery() < 3.10 && (!low_batt_d_)){
  853. ::CommandParser::DoParse("play", "lowbatt4.wav");
  854. low_batt_d_ = true;
  855. }
  856. #endif
  857. //-------------------------------------------------------------------------// BC REVO LOW BATT TIERS
  858. #ifdef ENABLE_AUDIO
  859. if (track_player_ && !track_player_->isPlaying()) {
  860. track_player_.Free();
  861. }
  862. #endif
  863.  
  864. #ifndef DISABLE_COLOR_CHANGE
  865. #define TICK_ANGLE (M_PI * 2 / 12)
  866. switch (SaberBase::GetColorChangeMode()) {
  867. case SaberBase::COLOR_CHANGE_MODE_NONE:
  868. break;
  869. case SaberBase::COLOR_CHANGE_MODE_STEPPED: {
  870. float a = fusor.angle2() - current_tick_angle_;
  871. if (a > M_PI) a-=M_PI*2;
  872. if (a < -M_PI) a+=M_PI*2;
  873. if (a > TICK_ANGLE * 2/3) {
  874. current_tick_angle_ += TICK_ANGLE;
  875. if (current_tick_angle_ > M_PI) current_tick_angle_ -= M_PI * 2;
  876. STDOUT << "TICK+\n";
  877. SaberBase::UpdateVariation(1);
  878. }
  879. if (a < -TICK_ANGLE * 2/3) {
  880. current_tick_angle_ -= TICK_ANGLE;
  881. if (current_tick_angle_ < M_PI) current_tick_angle_ += M_PI * 2;
  882. STDOUT << "TICK-\n";
  883. SaberBase::UpdateVariation(-1);
  884. }
  885. break;
  886. }
  887. case SaberBase::COLOR_CHANGE_MODE_SMOOTH:
  888. float a = fmodf(fusor.angle2() - current_tick_angle_, M_PI * 2);
  889. SaberBase::SetVariation(0x7fff & (int32_t)(a * (32768 / (M_PI * 2))));
  890. break;
  891. }
  892. if (monitor.ShouldPrint(Monitoring::MonitorVariation)) {
  893. STDOUT << " variation = " << SaberBase::GetCurrentVariation()
  894. << " ccmode = " << SaberBase::GetColorChangeMode()
  895. // << " color = " << current_config->blade1->current_style()->getColor(0)
  896. << "\n";
  897. }
  898.  
  899. #endif
  900.  
  901.  
  902. #ifdef IDLE_OFF_TIME
  903. if (SaberBase::IsOn() ||
  904. (current_style() && current_style()->Charging())) {
  905. last_on_time_ = millis();
  906. }
  907. if (millis() - last_on_time_ > IDLE_OFF_TIME) {
  908. SaberBase::DoOff(OFF_IDLE);
  909. last_on_time_ = millis();
  910. }
  911. #endif
  912.  
  913. PollSaveColorChange();
  914. }
  915.  
  916. #ifdef IDLE_OFF_TIME
  917. uint32_t last_on_time_;
  918. #endif
  919.  
  920. #ifndef DISABLE_COLOR_CHANGE
  921. void ToggleColorChangeMode() {
  922. if (!current_style()) return;
  923. if (SaberBase::GetColorChangeMode() == SaberBase::COLOR_CHANGE_MODE_NONE) {
  924. current_tick_angle_ = fusor.angle2();
  925. bool handles_color_change = false;
  926. #define CHECK_SUPPORTS_COLOR_CHANGE(N) \
  927. handles_color_change |= current_config->blade##N->current_style() && current_config->blade##N->current_style()->IsHandled(HANDLED_FEATURE_CHANGE_TICKED);
  928. ONCEPERBLADE(CHECK_SUPPORTS_COLOR_CHANGE)
  929. if (!handles_color_change) {
  930. STDOUT << "Entering smooth color change mode.\n";
  931. current_tick_angle_ -= SaberBase::GetCurrentVariation() * M_PI * 2 / 32768;
  932. current_tick_angle_ = fmodf(current_tick_angle_, M_PI * 2);
  933.  
  934. SaberBase::SetColorChangeMode(SaberBase::COLOR_CHANGE_MODE_SMOOTH);
  935. } else {
  936. #ifdef COLOR_CHANGE_DIRECT
  937. STDOUT << "Color change, TICK+\n";
  938. SaberBase::UpdateVariation(1);
  939. #else
  940. STDOUT << "Entering stepped color change mode.\n";
  941. SaberBase::SetColorChangeMode(SaberBase::COLOR_CHANGE_MODE_STEPPED);
  942. #endif
  943. }
  944. } else {
  945. STDOUT << "Color change mode done, variation = " << SaberBase::GetCurrentVariation() << "\n";
  946. SaberBase::SetColorChangeMode(SaberBase::COLOR_CHANGE_MODE_NONE);
  947. }
  948. }
  949. #endif // DISABLE_COLOR_CHANGE
  950.  
  951. void PrintButton(uint32_t b) {
  952. if (b & BUTTON_POWER) STDOUT.print("Power");
  953. if (b & BUTTON_AUX) STDOUT.print("Aux");
  954. if (b & BUTTON_AUX2) STDOUT.print("Aux2");
  955. if (b & BUTTON_UP) STDOUT.print("Up");
  956. if (b & BUTTON_DOWN) STDOUT.print("Down");
  957. if (b & BUTTON_LEFT) STDOUT.print("Left");
  958. if (b & BUTTON_RIGHT) STDOUT.print("Right");
  959. if (b & BUTTON_SELECT) STDOUT.print("Select");
  960. if (b & BUTTON_BLADE_DETECT) STDOUT.print("Select");
  961. if (b & MODE_ON) STDOUT.print("On");
  962. }
  963.  
  964. void PrintEvent(uint32_t e) {
  965. int cnt = 0;
  966. if (e >= EVENT_FIRST_PRESSED &&
  967. e <= EVENT_FOURTH_CLICK_LONG) {
  968. cnt = (e - EVENT_PRESSED) / (EVENT_SECOND_PRESSED - EVENT_FIRST_PRESSED);
  969. e -= (EVENT_SECOND_PRESSED - EVENT_FIRST_PRESSED) * cnt;
  970. }
  971. switch (e) {
  972. case EVENT_NONE: STDOUT.print("None"); break;
  973. case EVENT_PRESSED: STDOUT.print("Pressed"); break;
  974. case EVENT_RELEASED: STDOUT.print("Released"); break;
  975. case EVENT_HELD: STDOUT.print("Held"); break;
  976. case EVENT_HELD_MEDIUM: STDOUT.print("HeldMedium"); break;
  977. case EVENT_HELD_LONG: STDOUT.print("HeldLong"); break;
  978. case EVENT_CLICK_SHORT: STDOUT.print("Shortclick"); break;
  979. case EVENT_CLICK_LONG: STDOUT.print("Longclick"); break;
  980. case EVENT_SAVED_CLICK_SHORT: STDOUT.print("SavedShortclick"); break;
  981. case EVENT_LATCH_ON: STDOUT.print("On"); break;
  982. case EVENT_LATCH_OFF: STDOUT.print("Off"); break;
  983. case EVENT_STAB: STDOUT.print("Stab"); break;
  984. case EVENT_SWING: STDOUT.print("Swing"); break;
  985. case EVENT_SHAKE: STDOUT.print("Shake"); break;
  986. case EVENT_TWIST: STDOUT.print("Twist"); break;
  987. case EVENT_CLASH: STDOUT.print("Clash"); break;
  988. case EVENT_THRUST: STDOUT.print("Thrust"); break;
  989. case EVENT_PUSH: STDOUT.print("Push"); break;
  990. default: STDOUT.print("?"); STDOUT.print(e); break;
  991. }
  992. if (cnt) {
  993. STDOUT.print('#');
  994. STDOUT.print(cnt);
  995. }
  996. }
  997.  
  998. void PrintEvent(enum BUTTON button, EVENT event) {
  999. STDOUT.print("EVENT: ");
  1000. if (button) {
  1001. PrintButton(button);
  1002. STDOUT.print("-");
  1003. }
  1004. PrintEvent(event);
  1005. if (current_modifiers & ~button) {
  1006. STDOUT.print(" mods ");
  1007. PrintButton(current_modifiers);
  1008. }
  1009. if (IsOn()) STDOUT.print(" ON");
  1010. STDOUT.print(" millis=");
  1011. STDOUT.println(millis());
  1012. }
  1013.  
  1014. bool Parse(const char *cmd, const char* arg) override {
  1015. if (!strcmp(cmd, "id")) {
  1016. id();
  1017. return true;
  1018. }
  1019. if (!strcmp(cmd, "scanid")) {
  1020. FindBladeAgain();
  1021. return true;
  1022. }
  1023. if (!strcmp(cmd, "on")) {
  1024. On();
  1025. return true;
  1026. }
  1027. if (!strcmp(cmd, "off")) {
  1028. Off();
  1029. return true;
  1030. }
  1031. if (!strcmp(cmd, "get_on")) {
  1032. STDOUT.println(IsOn());
  1033. return true;
  1034. }
  1035. if (!strcmp(cmd, "clash")) {
  1036. Clash(false);
  1037. return true;
  1038. }
  1039. if (!strcmp(cmd, "stab")) {
  1040. Clash(true);
  1041. return true;
  1042. }
  1043. if (!strcmp(cmd, "force")) {
  1044. SaberBase::DoForce();
  1045. return true;
  1046. }
  1047. if (!strcmp(cmd, "blast")) {
  1048. // Avoid the base and the very tip.
  1049. // TODO: Make blast only appear on one blade!
  1050. SaberBase::DoBlast();
  1051. return true;
  1052. }
  1053. if (!strcmp(cmd, "lock") || !strcmp(cmd, "lockup")) {
  1054. STDOUT.print("Lockup ");
  1055. if (SaberBase::Lockup() == SaberBase::LOCKUP_NONE) {
  1056. SaberBase::SetLockup(SaberBase::LOCKUP_NORMAL);
  1057. SaberBase::DoBeginLockup();
  1058. STDOUT.println("ON");
  1059. } else {
  1060. SaberBase::DoEndLockup();
  1061. SaberBase::SetLockup(SaberBase::LOCKUP_NONE);
  1062. STDOUT.println("OFF");
  1063. }
  1064. return true;
  1065. }
  1066. if (!strcmp(cmd, "drag")) {
  1067. STDOUT.print("Drag ");
  1068. if (SaberBase::Lockup() == SaberBase::LOCKUP_NONE) {
  1069. SaberBase::SetLockup(SaberBase::LOCKUP_DRAG);
  1070. SaberBase::DoBeginLockup();
  1071. STDOUT.println("ON");
  1072. } else {
  1073. SaberBase::DoEndLockup();
  1074. SaberBase::SetLockup(SaberBase::LOCKUP_NONE);
  1075. STDOUT.println("OFF");
  1076. }
  1077. return true;
  1078. }
  1079. if (!strcmp(cmd, "lblock") || !strcmp(cmd, "lb")) {
  1080. STDOUT.print("lblock ");
  1081. if (SaberBase::Lockup() == SaberBase::LOCKUP_NONE) {
  1082. SaberBase::SetLockup(SaberBase::LOCKUP_LIGHTNING_BLOCK);
  1083. SaberBase::DoBeginLockup();
  1084. STDOUT.println("ON");
  1085. } else {
  1086. SaberBase::DoEndLockup();
  1087. SaberBase::SetLockup(SaberBase::LOCKUP_NONE);
  1088. STDOUT.println("OFF");
  1089. }
  1090. return true;
  1091. }
  1092. if (!strcmp(cmd, "melt")) {
  1093. STDOUT.print("melt ");
  1094. if (SaberBase::Lockup() == SaberBase::LOCKUP_NONE) {
  1095. SaberBase::SetLockup(SaberBase::LOCKUP_MELT);
  1096. SaberBase::DoBeginLockup();
  1097. STDOUT.println("ON");
  1098. } else {
  1099. SaberBase::DoEndLockup();
  1100. SaberBase::SetLockup(SaberBase::LOCKUP_NONE);
  1101. STDOUT.println("OFF");
  1102. }
  1103. return true;
  1104. }
  1105.  
  1106. #ifdef ENABLE_AUDIO
  1107.  
  1108. #ifndef DISABLE_DIAGNOSTIC_COMMANDS
  1109. if (!strcmp(cmd, "beep")) {
  1110. // Close Encounters // BC added songs :)
  1111. // beeper.Beep(0.5, 293.66 * 2);
  1112. // beeper.Beep(0.5, 329.33 * 2);
  1113. // beeper.Beep(0.5, 261.63 * 2);
  1114. // beeper.Beep(0.5, 130.81 * 2);
  1115. // beeper.Beep(1.0, 196.00 * 2);
  1116.  
  1117. // Imperial March
  1118. // beeper.Beep(0.45, 195);
  1119. // beeper.Beep(0.2, 30000);
  1120. // beeper.Beep(0.45, 195);
  1121. // beeper.Beep(0.2, 30000);
  1122. // beeper.Beep(0.45, 195);
  1123. // beeper.Beep(0.2, 30000);
  1124. // beeper.Beep(0.45, 155);
  1125. // beeper.Beep(0.15, 233);
  1126. // beeper.Beep(0.45, 195);
  1127. // beeper.Beep(0.2, 30000);
  1128. // beeper.Beep(0.45, 155);
  1129. // beeper.Beep(0.15, 233);
  1130. // beeper.Beep(0.6, 195);
  1131.  
  1132. // Star Wars Theme
  1133. beeper.Beep(1.0, 475);
  1134. beeper.Beep(0.5, 693);
  1135. beeper.Beep(0.16, 625);
  1136. beeper.Beep(0.16, 595);
  1137. beeper.Beep(0.16, 525);
  1138. beeper.Beep(1.1, 950);
  1139. beeper.Beep(0.5, 693);
  1140. beeper.Beep(0.16, 625);
  1141. beeper.Beep(0.16, 595);
  1142. beeper.Beep(0.16, 525);
  1143. beeper.Beep(1.1, 950);
  1144. beeper.Beep(0.5, 693);
  1145. beeper.Beep(0.16, 625);
  1146. beeper.Beep(0.16, 595);
  1147. beeper.Beep(0.16, 625);
  1148. beeper.Beep(1.1, 525);
  1149. return true;
  1150. }
  1151. #endif
  1152. if (!strcmp(cmd, "play")) {
  1153. if (!arg) {
  1154. StartOrStopTrack();
  1155. return true;
  1156. }
  1157. MountSDCard();
  1158. EnableAmplifier();
  1159. RefPtr<BufferedWavPlayer> player = GetFreeWavPlayer();
  1160. if (player) {
  1161. STDOUT.print("Playing ");
  1162. STDOUT.println(arg);
  1163. if (!player->PlayInCurrentDir(arg))
  1164. player->Play(arg);
  1165. } else {
  1166. STDOUT.println("No available WAV players.");
  1167. }
  1168. return true;
  1169. }
  1170. if (!strcmp(cmd, "play_track")) {
  1171. if (!arg) {
  1172. StartOrStopTrack();
  1173. return true;
  1174. }
  1175. if (track_player_) {
  1176. track_player_->Stop();
  1177. track_player_.Free();
  1178. }
  1179. MountSDCard();
  1180. EnableAmplifier();
  1181. track_player_ = GetFreeWavPlayer();
  1182. if (track_player_) {
  1183. STDOUT.print("Playing ");
  1184. STDOUT.println(arg);
  1185. if (!track_player_->PlayInCurrentDir(arg))
  1186. track_player_->Play(arg);
  1187. } else {
  1188. STDOUT.println("No available WAV players.");
  1189. }
  1190. return true;
  1191. }
  1192. if (!strcmp(cmd, "stop_track")) {
  1193. if (track_player_) {
  1194. track_player_->Stop();
  1195. track_player_.Free();
  1196. }
  1197. return true;
  1198. }
  1199. if (!strcmp(cmd, "get_track")) {
  1200. if (track_player_) {
  1201. STDOUT.println(track_player_->Filename());
  1202. }
  1203. return true;
  1204. }
  1205. #ifndef DISABLE_DIAGNOSTIC_COMMANDS
  1206. if (!strcmp(cmd, "volumes")) {
  1207. for (size_t unit = 0; unit < NELEM(wav_players); unit++) {
  1208. STDOUT.print(" Unit ");
  1209. STDOUT.print(unit);
  1210. STDOUT.print(" Volume ");
  1211. STDOUT.println(wav_players[unit].volume());
  1212. }
  1213. return true;
  1214. }
  1215. #endif
  1216. #ifndef DISABLE_DIAGNOSTIC_COMMANDS
  1217. if (!strcmp(cmd, "buffered")) {
  1218. for (size_t unit = 0; unit < NELEM(wav_players); unit++) {
  1219. STDOUT.print(" Unit ");
  1220. STDOUT.print(unit);
  1221. STDOUT.print(" Buffered: ");
  1222. STDOUT.println(wav_players[unit].buffered());
  1223. }
  1224. return true;
  1225. }
  1226. #endif
  1227.  
  1228. #endif // enable sound
  1229. if (!strcmp(cmd, "cd")) {
  1230. chdir(arg);
  1231. SaberBase::DoNewFont();
  1232. return true;
  1233. }
  1234. #if 0
  1235. if (!strcmp(cmd, "mkdir")) {
  1236. SD.mkdir(arg);
  1237. return true;
  1238. }
  1239. #endif
  1240. if (!strcmp(cmd, "pwd")) {
  1241. for (const char* dir = current_directory; dir; dir = next_current_directory(dir)) {
  1242. STDOUT.println(dir);
  1243. }
  1244. return true;
  1245. }
  1246. if (!strcmp(cmd, "n") || (!strcmp(cmd, "next") && arg && (!strcmp(arg, "preset") || !strcmp(arg, "pre")))) {
  1247. next_preset();
  1248. return true;
  1249. }
  1250. if (!strcmp(cmd, "p") || (!strcmp(cmd, "prev") && arg && (!strcmp(arg, "preset") || !strcmp(arg, "pre")))) {
  1251. previous_preset();
  1252. return true;
  1253. }
  1254. if (!strcmp(cmd, "rotate")) {
  1255. rotate_presets();
  1256. return true;
  1257. }
  1258. if (!strcmp(cmd, "message") && arg) {
  1259. SaberBase::DoMessage(arg);
  1260. return true;
  1261. }
  1262.  
  1263. if (!strcmp(cmd, "list_presets")) {
  1264. CurrentPreset tmp;
  1265. for (int i = 0; ; i++) {
  1266. tmp.SetPreset(i);
  1267. if (tmp.preset_num != i) break;
  1268. tmp.Print();
  1269. }
  1270. return true;
  1271. }
  1272.  
  1273. if (!strcmp(cmd, "set_font") && arg) {
  1274. current_preset_.font = mkstr(arg);
  1275. current_preset_.Save();
  1276. return true;
  1277. }
  1278.  
  1279. if (!strcmp(cmd, "set_track") && arg) {
  1280. current_preset_.track = mkstr(arg);
  1281. current_preset_.Save();
  1282. return true;
  1283. }
  1284.  
  1285. if (!strcmp(cmd, "set_name") && arg) {
  1286. current_preset_.name = mkstr(arg);
  1287. current_preset_.Save();
  1288. return true;
  1289. }
  1290.  
  1291. #define SET_STYLE_CMD(N) \
  1292. if (!strcmp(cmd, "set_style" #N) && arg) { \
  1293. current_preset_.current_style##N = mkstr(arg); \
  1294. current_preset_.Save(); \
  1295. return true; \
  1296. }
  1297. ONCEPERBLADE(SET_STYLE_CMD)
  1298.  
  1299. if (!strcmp(cmd, "move_preset") && arg) {
  1300. int32_t pos = strtol(arg, NULL, 0);
  1301. current_preset_.SaveAt(pos);
  1302. return true;
  1303. }
  1304.  
  1305. if (!strcmp(cmd, "duplicate_preset") && arg) {
  1306. int32_t pos = strtol(arg, NULL, 0);
  1307. current_preset_.preset_num = -1;
  1308. current_preset_.SaveAt(pos);
  1309. return true;
  1310. }
  1311.  
  1312. if (!strcmp(cmd, "delete_preset") && arg) {
  1313. current_preset_.SaveAt(-1);
  1314. return true;
  1315. }
  1316.  
  1317. if (!strcmp(cmd, "show_current_preset")) {
  1318. current_preset_.Print();
  1319. return true;
  1320. }
  1321.  
  1322. if (!strcmp(cmd, "get_preset")) {
  1323. STDOUT.println(current_preset_.preset_num);
  1324. return true;
  1325. }
  1326. if (!strcmp(cmd, "get_volume")) {
  1327. #ifdef ENABLE_AUDIO
  1328. STDOUT.println(dynamic_mixer.get_volume());
  1329. #else
  1330. STDOUT.println(0);
  1331. #endif
  1332. return true;
  1333. }
  1334. if (!strcmp(cmd, "set_volume") && arg) {
  1335. #ifdef ENABLE_AUDIO
  1336. int32_t volume = strtol(arg, NULL, 0);
  1337. if (volume >= 0 && volume <= 3000) {
  1338. dynamic_mixer.set_volume(volume);
  1339. PollSaveColorChange();
  1340. }
  1341. #endif
  1342. return true;
  1343. }
  1344. if (!strcmp(cmd, "mute")) {
  1345. SetMute(true);
  1346. return true;
  1347. }
  1348. if (!strcmp(cmd, "unmute")) {
  1349. SetMute(false);
  1350. return true;
  1351. }
  1352. if (!strcmp(cmd, "toggle_mute")) {
  1353. if (!SetMute(true)) SetMute(false);
  1354. return true;
  1355. }
  1356.  
  1357. if (!strcmp(cmd, "set_preset") && arg) {
  1358. int preset = strtol(arg, NULL, 0);
  1359. SetPreset(preset, true);
  1360. return true;
  1361. }
  1362.  
  1363. if (!strcmp(cmd, "change_preset") && arg) {
  1364. int preset = strtol(arg, NULL, 0);
  1365. if (preset != current_preset_.preset_num) {
  1366. SetPreset(preset, true);
  1367. }
  1368. return true;
  1369. }
  1370.  
  1371. #ifndef DISABLE_COLOR_CHANGE
  1372. if (arg && (!strcmp(cmd, "var") || !strcmp(cmd, "variation"))) {
  1373. size_t variation = strtol(arg, NULL, 0);
  1374. SaberBase::SetVariation(variation);
  1375. return true;
  1376. }
  1377. if (!strcmp(cmd, "ccmode")) {
  1378. ToggleColorChangeMode();
  1379. return true;
  1380. }
  1381. #endif
  1382.  
  1383. #ifdef ENABLE_SD
  1384. if (!strcmp(cmd, "list_tracks")) {
  1385. // Tracks are must be in: tracks/*.wav or */tracks/*.wav
  1386. LOCK_SD(true);
  1387. ListTracks("tracks");
  1388. for (LSFS::Iterator iter("/"); iter; ++iter) {
  1389. if (iter.isdir()) {
  1390. PathHelper path(iter.name(), "tracks");
  1391. ListTracks(path);
  1392. }
  1393. }
  1394. LOCK_SD(false);
  1395. return true;
  1396. }
  1397.  
  1398. if (!strcmp(cmd, "list_fonts")) {
  1399. LOCK_SD(true);
  1400. for (LSFS::Iterator iter("/"); iter; ++iter) {
  1401. if (iter.isdir()) {
  1402. char fname[128];
  1403. strcpy(fname, iter.name());
  1404. strcat(fname, "/");
  1405. char* fend = fname + strlen(fname);
  1406. bool isfont = false;
  1407. if (!isfont) {
  1408. strcpy(fend, "hum.wav");
  1409. isfont = LSFS::Exists(fname);
  1410. }
  1411. if (!isfont) {
  1412. strcpy(fend, "hum01.wav");
  1413. isfont = LSFS::Exists(fname);
  1414. }
  1415. if (!isfont) {
  1416. strcpy(fend, "hum");
  1417. isfont = LSFS::Exists(fname);
  1418. }
  1419. if (isfont) {
  1420. STDOUT.println(iter.name());
  1421. }
  1422. }
  1423. }
  1424. LOCK_SD(false);
  1425. return true;
  1426. }
  1427. #endif
  1428. return false;
  1429. }
  1430. void Help() override {
  1431. STDOUT.println(" clash - trigger a clash");
  1432. STDOUT.println(" on/off - turn saber on/off");
  1433. STDOUT.println(" force - trigger a force push");
  1434. STDOUT.println(" blast - trigger a blast");
  1435. STDOUT.println(" stab - trigger a stab");
  1436. STDOUT.println(" lock - begin/end lockup");
  1437. STDOUT.println(" lblock/lb - begin/end lightning block");
  1438. STDOUT.println(" melt - begin/end melt");
  1439. #ifdef ENABLE_AUDIO
  1440. STDOUT.println(" pwd - print current directory");
  1441. STDOUT.println(" cd directory - change directory, and sound font");
  1442. STDOUT.println(" play filename - play file");
  1443. STDOUT.println(" next/prev font - walk through directories in alphabetical order");
  1444. STDOUT.println(" next/prev pre[set] - walk through presets.");
  1445. STDOUT.println(" beep - play a beep");
  1446. #endif
  1447. }
  1448.  
  1449. virtual bool Event(enum BUTTON button, EVENT event) {
  1450. PrintEvent(button, event);
  1451.  
  1452. switch (event) {
  1453. case EVENT_RELEASED:
  1454. clash_pending_ = false;
  1455. case EVENT_PRESSED:
  1456. IgnoreClash(50); // ignore clashes to prevent buttons from causing clashes
  1457. default:
  1458. break;
  1459. }
  1460.  
  1461. if (Event2(button, event, current_modifiers | (IsOn() ? MODE_ON : MODE_OFF))) {
  1462. current_modifiers = 0;
  1463. return true;
  1464. }
  1465. if (Event2(button, event, MODE_ANY_BUTTON | (IsOn() ? MODE_ON : MODE_OFF))) {
  1466. // Not matching modifiers, so no need to clear them.
  1467. current_modifiers &= ~button;
  1468. return true;
  1469. }
  1470. return false;
  1471. }
  1472.  
  1473. virtual bool Event2(enum BUTTON button, EVENT event, uint32_t modifiers) = 0;
  1474.  
  1475. protected:
  1476. CurrentPreset current_preset_;
  1477. LoopCounter accel_loop_counter_;
  1478. };
  1479.  
  1480. #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement