From 60d7b4e3466cc291043732e60825a3b4db535a4e Mon Sep 17 00:00:00 2001 From: NaiJi Date: Wed, 8 Sep 2021 22:05:56 +0300 Subject: [PATCH] Upgrade bpm calculator, try implement slider animation --- include/application.h | 1 + include/tools/bpmcalculator.h | 21 +++++- src/application.cpp | 8 ++- src/gui/editor.cpp | 1 + src/gui/mainmenu.cpp | 13 ++-- src/gui/mainmenu.h | 1 + src/gui/widgets/bpmcalculatorwidget.cpp | 10 ++- src/gui/widgets/bpmslider.cpp | 14 ++++ src/gui/widgets/bpmslider.h | 10 ++- src/gui/widgets/button.cpp | 10 +++ src/gui/widgets/button.h | 5 +- src/gui/widgets/group.cpp | 15 +++++ src/gui/widgets/group.h | 8 ++- src/gui/widgets/menubar.cpp | 10 +++ src/gui/widgets/menubar.h | 8 ++- src/gui/widgets/menudrop.cpp | 9 +++ src/gui/widgets/menudrop.h | 8 ++- src/gui/widgets/menuseparator.cpp | 9 +++ src/gui/widgets/menuseparator.h | 8 ++- src/gui/widgets/widget.h | 8 ++- src/gui/widgets/window.cpp | 9 +++ src/gui/widgets/window.h | 8 ++- src/tools/bpmcalculator.cpp | 90 ++++++++++++++++++++----- 23 files changed, 246 insertions(+), 38 deletions(-) diff --git a/include/application.h b/include/application.h index 4c1e030..5d690d1 100644 --- a/include/application.h +++ b/include/application.h @@ -25,6 +25,7 @@ private: std::array, GUIState::Tag::AMOUNT> _states; std::vector> _state_stack; + sf::RenderWindow _game_window; std::shared_ptr _game; diff --git a/include/tools/bpmcalculator.h b/include/tools/bpmcalculator.h index d25d9e7..7e2325d 100644 --- a/include/tools/bpmcalculator.h +++ b/include/tools/bpmcalculator.h @@ -8,16 +8,33 @@ class BPMCalculator { public: explicit BPMCalculator(const std::shared_ptr& music); + void setMusic(const std::shared_ptr& music); std::shared_ptr music() const; void start(); - + void stop(); void click(); - int getCurrentApproximation() const; + float fetchCurrentBPMApproximation(); + + microsec getStartingOffset() const; + void setStartingOffset(microsec offset); + void moveStartingOffsetBy(microsec shift); + + microsec fetchTimeUntilNextBeat(); + microsec fetchBeatInterval(); private: + bool _need_recalculate; + bool _calculating; + std::shared_ptr _music; std::vector _deltas; microsec _previous_click_offset; + microsec _first_click_offset; + + int _approximated_bpm; + + inline float calculateBPM(microsec all_microseconds, std::size_t beats_amount) const; + inline void reset(); }; diff --git a/src/application.cpp b/src/application.cpp index 1dc4fd8..55ed761 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -26,12 +26,18 @@ Application::Application() : _game_window.setMouseCursorGrabbed(false); _game_window.setVerticalSyncEnabled(true); - MainMenu::Callbacks callbacks = {[&](){ pushState(GUIState::Tag::EDITOR); }}; + MainMenu::Callbacks callbacks = + { + [&](){ pushState(GUIState::Tag::GAME); }, + [&](){ pushState(GUIState::Tag::EDITOR); } + }; + Editor::Callbacks editor_callbacks = {[&](){ popState(); }}; const auto main_menu = std::make_shared(_game_window, std::move(callbacks), _font_holder); const auto game_state = std::make_shared(_game_window, _game, GameState::Callbacks()); const auto editor = std::make_shared(_game_window, std::move(editor_callbacks), std::make_unique(), _font_holder); + _states[GUIState::Tag::MAIN_MENU] = main_menu; _states[GUIState::Tag::GAME] = game_state; _states[GUIState::Tag::EDITOR] = editor; diff --git a/src/gui/editor.cpp b/src/gui/editor.cpp index 2698e03..a99704d 100644 --- a/src/gui/editor.cpp +++ b/src/gui/editor.cpp @@ -106,5 +106,6 @@ void Editor::enter() void Editor::leave() { _group.reset(); + _bpm_calculator.reset(); } diff --git a/src/gui/mainmenu.cpp b/src/gui/mainmenu.cpp index e393740..6724262 100644 --- a/src/gui/mainmenu.cpp +++ b/src/gui/mainmenu.cpp @@ -9,18 +9,23 @@ MainMenu::MainMenu(sf::RenderWindow& game_window, Callbacks&& callbacks, const F const float window_width = game_window.getSize().x; const float window_height = game_window.getSize().y; - std::shared_ptr button_start = std::make_shared("Start", font_holder.get(Fonts::Id::GUI), 48); - button_start->setRect(sf::FloatRect(window_width / 3., window_height / 7. * 2, window_width / 3., window_height / 7.)); + auto button_start = std::make_shared("Start", font_holder.get(Fonts::Id::GUI), 48); + button_start->setRect(sf::FloatRect(window_width / 3., window_height / 7., window_width / 3., window_height / 7.)); button_start->setCallback(callbacks.onAppendGameState); - std::shared_ptr button_exit = std::make_shared("Exit", font_holder.get(Fonts::Id::GUI), 48); - button_exit->setRect(sf::FloatRect(window_width / 3., window_height / 7. * 4, window_width / 3., window_height / 7.)); + auto button_editor = std::make_shared("Editor", font_holder.get(Fonts::Id::GUI), 48); + button_editor->setRect(sf::FloatRect(window_width / 3., window_height / 7. * 3, window_width / 3., window_height / 7.)); + button_editor->setCallback(callbacks.onAppendEditorState); + + auto button_exit = std::make_shared("Exit", font_holder.get(Fonts::Id::GUI), 48); + button_exit->setRect(sf::FloatRect(window_width / 3., window_height / 7. * 5, window_width / 3., window_height / 7.)); button_exit->setCallback([&]() { _game_window.close(); }); _buttons->addChild(button_start); + _buttons->addChild(button_editor); _buttons->addChild(button_exit); } diff --git a/src/gui/mainmenu.h b/src/gui/mainmenu.h index a2d4c78..f5fee9e 100644 --- a/src/gui/mainmenu.h +++ b/src/gui/mainmenu.h @@ -13,6 +13,7 @@ public: struct Callbacks { std::function onAppendGameState; + std::function onAppendEditorState; }; explicit MainMenu(sf::RenderWindow& game_window, Callbacks&& callbacks, const FontHolder &font_holder); diff --git a/src/gui/widgets/bpmcalculatorwidget.cpp b/src/gui/widgets/bpmcalculatorwidget.cpp index 1894a34..a3585e3 100644 --- a/src/gui/widgets/bpmcalculatorwidget.cpp +++ b/src/gui/widgets/bpmcalculatorwidget.cpp @@ -34,10 +34,18 @@ void BPMCalculatorWidget::update(const sf::Time& dt) { Window::update(dt); - const auto approximation = _bpm_calculator->getCurrentApproximation(); + const auto approximation = _bpm_calculator->fetchCurrentBPMApproximation(); if (approximation != 0) { _bpm_value.setString(std::to_string(approximation)); + + const microsec until_beat = _bpm_calculator->fetchTimeUntilNextBeat(); + const microsec beat_interval = _bpm_calculator->fetchBeatInterval(); + + const auto time_relation = beat_interval / until_beat; + const auto slider_path_left = Window::rect().width / time_relation; + + _slider->setTickPosition(slider_path_left); } } diff --git a/src/gui/widgets/bpmslider.cpp b/src/gui/widgets/bpmslider.cpp index 4b81ffc..f459cb3 100644 --- a/src/gui/widgets/bpmslider.cpp +++ b/src/gui/widgets/bpmslider.cpp @@ -49,3 +49,17 @@ bool BPMSlider::isUnderMouse(int mouse_x, int mouse_y) const return mouse_x == mouse_y; // just to compile } +sf::FloatRect BPMSlider::rect() const +{ + return _slider_background.getGlobalBounds(); +} + +sf::Vector2f BPMSlider::position() const +{ + return _slider_background.getPosition(); +} + +void BPMSlider::setTickPosition(float x_position) +{ + _slider_tick.setPosition(_slider_background.getPosition().x + x_position, _slider_tick.getPosition().y); +} diff --git a/src/gui/widgets/bpmslider.h b/src/gui/widgets/bpmslider.h index be56969..facc018 100644 --- a/src/gui/widgets/bpmslider.h +++ b/src/gui/widgets/bpmslider.h @@ -13,11 +13,17 @@ public: virtual void input(const sf::Event& event) override; virtual void update(const sf::Time& dt) override; virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override; - virtual void setRect(const sf::FloatRect& rect) override; - virtual void setPosition(const sf::Vector2f& position) override; virtual void move(const sf::Vector2f& delta) override; virtual bool isUnderMouse(int mouse_x, int mouse_y) const override; + virtual void setRect(const sf::FloatRect& rect) override; + virtual sf::FloatRect rect() const override; + + virtual void setPosition(const sf::Vector2f& position) override; + virtual sf::Vector2f position() const override; + + void setTickPosition(float x_position); + private: sf::RectangleShape _slider_background; sf::RectangleShape _slider_tick; diff --git a/src/gui/widgets/button.cpp b/src/gui/widgets/button.cpp index 371b2fa..742b6bb 100644 --- a/src/gui/widgets/button.cpp +++ b/src/gui/widgets/button.cpp @@ -54,3 +54,13 @@ void Button::setText(const std::string& text) { _button_text.setString(text); } + +sf::FloatRect Button::rect() const +{ + return _button_content.getGlobalBounds(); +} + +sf::Vector2f Button::position() const +{ + return _button_content.getPosition(); +} diff --git a/src/gui/widgets/button.h b/src/gui/widgets/button.h index cf121aa..5aeac8d 100644 --- a/src/gui/widgets/button.h +++ b/src/gui/widgets/button.h @@ -13,11 +13,14 @@ public: virtual void input(const sf::Event& event) override = 0; virtual void update(const sf::Time& dt) override final; virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override final; - virtual void setPosition(const sf::Vector2f& position) override final; virtual void move(const sf::Vector2f& delta) override final; virtual bool isUnderMouse(int mouse_x, int mouse_y) const override final; virtual void setRect(const sf::FloatRect& rect) override; + virtual sf::FloatRect rect() const override; + + virtual void setPosition(const sf::Vector2f& position) override; + virtual sf::Vector2f position() const override; virtual void setText(const std::string& text); diff --git a/src/gui/widgets/group.cpp b/src/gui/widgets/group.cpp index 332dcf0..03d5944 100644 --- a/src/gui/widgets/group.cpp +++ b/src/gui/widgets/group.cpp @@ -38,3 +38,18 @@ bool Group::isUnderMouse(int mouse_x, int mouse_y) const { return _rect.contains(mouse_x, mouse_y); } + +sf::FloatRect Group::rect() const +{ + return _rect; +} + +sf::Vector2f Group::position() const +{ + return sf::Vector2f + { + _rect.top, + _rect.left + }; +} + diff --git a/src/gui/widgets/group.h b/src/gui/widgets/group.h index b3d9dc4..4f67bd1 100644 --- a/src/gui/widgets/group.h +++ b/src/gui/widgets/group.h @@ -8,11 +8,15 @@ public: virtual void input(const sf::Event& event) override; virtual void update(const sf::Time& dt) override; virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override; - virtual void setRect(const sf::FloatRect& rect) override; - virtual void setPosition(const sf::Vector2f& position) override; virtual void move(const sf::Vector2f& delta) override; virtual bool isUnderMouse(int mouse_x, int mouse_y) const override; + virtual void setRect(const sf::FloatRect& rect) override; + virtual sf::FloatRect rect() const override; + + virtual void setPosition(const sf::Vector2f& position) override; + virtual sf::Vector2f position() const override; + private: sf::FloatRect _rect; }; diff --git a/src/gui/widgets/menubar.cpp b/src/gui/widgets/menubar.cpp index 965c9d3..fa14f56 100644 --- a/src/gui/widgets/menubar.cpp +++ b/src/gui/widgets/menubar.cpp @@ -113,3 +113,13 @@ void MenuBar::setVisibility(bool is_visible) for (auto& submenu : _submenus) submenu->setVisibility(false); } + +sf::FloatRect MenuBar::rect() const +{ + return _bar_rect.getGlobalBounds(); +} + +sf::Vector2f MenuBar::position() const +{ + return _bar_rect.getPosition(); +} diff --git a/src/gui/widgets/menubar.h b/src/gui/widgets/menubar.h index 0183a59..fea7be5 100644 --- a/src/gui/widgets/menubar.h +++ b/src/gui/widgets/menubar.h @@ -14,12 +14,16 @@ public: virtual void input(const sf::Event& event) override; virtual void update(const sf::Time& dt) override; virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override; - virtual void setRect(const sf::FloatRect& rect) override; - virtual void setPosition(const sf::Vector2f& position) override; virtual void move(const sf::Vector2f& delta) override; virtual bool isUnderMouse(int mouse_x, int mouse_y) const override; virtual void setVisibility(bool is_visible = true) override; + virtual void setRect(const sf::FloatRect& rect) override; + virtual sf::FloatRect rect() const override; + + virtual void setPosition(const sf::Vector2f& position) override; + virtual sf::Vector2f position() const override; + void addRootSubMenu(std::string name, const std::shared_ptr& submenu); void addDependentSubmenu(const std::shared_ptr& submenu); diff --git a/src/gui/widgets/menudrop.cpp b/src/gui/widgets/menudrop.cpp index 222c6e1..1eebfbc 100644 --- a/src/gui/widgets/menudrop.cpp +++ b/src/gui/widgets/menudrop.cpp @@ -147,3 +147,12 @@ bool MenuDrop::isLocked() const return _is_locked; } +sf::FloatRect MenuDrop::rect() const +{ + return _content_rect.getGlobalBounds(); +} + +sf::Vector2f MenuDrop::position() const +{ + return _content_rect.getPosition(); +} diff --git a/src/gui/widgets/menudrop.h b/src/gui/widgets/menudrop.h index 0bbac32..59e7b04 100644 --- a/src/gui/widgets/menudrop.h +++ b/src/gui/widgets/menudrop.h @@ -13,12 +13,16 @@ public: virtual void input(const sf::Event& event) override; virtual void update(const sf::Time& dt) override; virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override; - virtual void setRect(const sf::FloatRect& rect) override; - virtual void setPosition(const sf::Vector2f& position) override; virtual void move(const sf::Vector2f& delta) override; virtual bool isUnderMouse(int mouse_x, int mouse_y) const override; virtual void setVisibility(bool is_visible = true) override; + virtual void setRect(const sf::FloatRect& rect) override; + virtual sf::FloatRect rect() const override; + + virtual void setPosition(const sf::Vector2f& position) override; + virtual sf::Vector2f position() const override; + void addPushButton(const std::shared_ptr& button); void addCascadeButton(const std::shared_ptr& button); void addSeparator(); diff --git a/src/gui/widgets/menuseparator.cpp b/src/gui/widgets/menuseparator.cpp index 249b537..abe0fb5 100644 --- a/src/gui/widgets/menuseparator.cpp +++ b/src/gui/widgets/menuseparator.cpp @@ -37,3 +37,12 @@ bool MenuSeparator::isUnderMouse(int mouse_x, int mouse_y) const return _is_visible && _rect.contains(mouse_x, mouse_y); } +sf::FloatRect MenuSeparator::rect() const +{ + return {}; +} + +sf::Vector2f MenuSeparator::position() const +{ + return {}; +} diff --git a/src/gui/widgets/menuseparator.h b/src/gui/widgets/menuseparator.h index 7456276..2412355 100644 --- a/src/gui/widgets/menuseparator.h +++ b/src/gui/widgets/menuseparator.h @@ -10,11 +10,15 @@ public: virtual void input(const sf::Event& event) override; virtual void update(const sf::Time& dt) override; virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override; - virtual void setRect(const sf::FloatRect& rect) override; - virtual void setPosition(const sf::Vector2f& position) override; virtual void move(const sf::Vector2f& delta) override; virtual bool isUnderMouse(int mouse_x, int mouse_y) const override; + virtual void setRect(const sf::FloatRect& rect) override; + virtual sf::FloatRect rect() const override; + + virtual void setPosition(const sf::Vector2f& position) override; + virtual sf::Vector2f position() const override; + private: sf::VertexArray _line; sf::FloatRect _rect; diff --git a/src/gui/widgets/widget.h b/src/gui/widgets/widget.h index 9091d4d..ac25b3b 100644 --- a/src/gui/widgets/widget.h +++ b/src/gui/widgets/widget.h @@ -14,11 +14,15 @@ public: virtual void input(const sf::Event& event) = 0; virtual void update(const sf::Time& dt) = 0; virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const = 0; - virtual void setRect(const sf::FloatRect& rect) = 0; - virtual void setPosition(const sf::Vector2f& position) = 0; virtual void move(const sf::Vector2f& delta) = 0; virtual bool isUnderMouse(int mouse_x, int mouse_y) const = 0; + virtual void setRect(const sf::FloatRect& rect) = 0; + virtual sf::FloatRect rect() const = 0; + + virtual void setPosition(const sf::Vector2f& position) = 0; + virtual sf::Vector2f position() const = 0; + virtual void setVisibility(bool is_visible = true); bool isVisible() const; diff --git a/src/gui/widgets/window.cpp b/src/gui/widgets/window.cpp index c918f9a..4155fce 100644 --- a/src/gui/widgets/window.cpp +++ b/src/gui/widgets/window.cpp @@ -108,3 +108,12 @@ void Window::addBarButton(const std::string &text, std::function cal addChild(b); } +sf::FloatRect Window::rect() const +{ + return _window_content.getGlobalBounds(); +} + +sf::Vector2f Window::position() const +{ + return _window_content.getPosition(); +} diff --git a/src/gui/widgets/window.h b/src/gui/widgets/window.h index ee52d40..0ff69ab 100644 --- a/src/gui/widgets/window.h +++ b/src/gui/widgets/window.h @@ -13,11 +13,15 @@ public: virtual void input(const sf::Event& event) override; virtual void update(const sf::Time& dt) override; virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override; - virtual void setRect(const sf::FloatRect& rect) override; - virtual void setPosition(const sf::Vector2f& position) override; virtual void move(const sf::Vector2f& delta) override; virtual bool isUnderMouse(int mouse_x, int mouse_y) const override final; + virtual void setRect(const sf::FloatRect& rect) override; + virtual sf::FloatRect rect() const override; + + virtual void setPosition(const sf::Vector2f& position) override; + virtual sf::Vector2f position() const override; + void addBarButton(const std::string& text, std::function callback); protected: diff --git a/src/tools/bpmcalculator.cpp b/src/tools/bpmcalculator.cpp index 118850b..fde8b9d 100644 --- a/src/tools/bpmcalculator.cpp +++ b/src/tools/bpmcalculator.cpp @@ -3,18 +3,27 @@ #include #include -constexpr microsec MICROSECONDS_IN_MINUTE = 60000000; +constexpr float MICROSECONDS_IN_MINUTE_f = 60000000.; BPMCalculator::BPMCalculator(const std::shared_ptr& music) : - _music(music), - _previous_click_offset(0) -{} + _music(music) +{ + reset(); +} -void BPMCalculator::setMusic(const std::shared_ptr& music) +void BPMCalculator::reset() { + _calculating = false; _deltas.clear(); _previous_click_offset = 0; + _approximated_bpm = 0; + _need_recalculate = true; +} + +void BPMCalculator::setMusic(const std::shared_ptr& music) +{ _music = music; + reset(); } std::shared_ptr BPMCalculator::music() const @@ -24,15 +33,23 @@ std::shared_ptr BPMCalculator::music() const void BPMCalculator::start() { - _deltas.clear(); - _previous_click_offset = 0; + reset(); + + _calculating = true; +} + +void BPMCalculator::stop() +{ + _calculating = false; } void BPMCalculator::click() { - const microsec click_offset = _music->fetchOffset(); + if (!_calculating) + return; - std::cout << click_offset << "\n\n\n\n"; + const microsec click_offset = _music->fetchOffset(); + _need_recalculate = true; if (_previous_click_offset == 0) { @@ -46,14 +63,57 @@ void BPMCalculator::click() _previous_click_offset = click_offset; } -int BPMCalculator::getCurrentApproximation() const +float BPMCalculator::fetchCurrentBPMApproximation() { + if (!_need_recalculate) + return _approximated_bpm; + + _need_recalculate = false; + const microsec sum = std::accumulate(_deltas.begin(), _deltas.end(), 0); + bool hasEnoughDeltas = _deltas.size() >= 8; - std::cout << "S: " << sum << " _deltas.size(): " << _deltas.size(); - std::cout << "\n " << (static_cast(sum) / static_cast(_deltas.size())) << '\n'; + _approximated_bpm = (!hasEnoughDeltas) + ? 0. + : calculateBPM(sum, _deltas.size()); + + return _approximated_bpm; +} - return (sum == 0 || _deltas.size() < 8) - ? 0. - : static_cast(static_cast(MICROSECONDS_IN_MINUTE) / (static_cast(sum) / static_cast(_deltas.size()))); +float BPMCalculator::calculateBPM(microsec all_microseconds, std::size_t beats_amount) const +{ + if (beats_amount == 0) + return 0; + + float relation = static_cast(all_microseconds) + / static_cast(beats_amount); + + return static_cast(1. / relation); +} + +microsec BPMCalculator::getStartingOffset() const +{ + return _first_click_offset; +} + +void BPMCalculator::setStartingOffset(microsec offset) +{ + _first_click_offset = offset; +} + +void BPMCalculator::moveStartingOffsetBy(microsec shift) +{ + _first_click_offset += shift; +} + +microsec BPMCalculator::fetchTimeUntilNextBeat() +{ + const microsec actual_offset = _music->fetchOffset() - _first_click_offset; + + return fetchBeatInterval() % actual_offset; +} + +microsec BPMCalculator::fetchBeatInterval() +{ + return static_cast(1. / fetchCurrentBPMApproximation()); }