Implement slider tick animation on BPM calculator

This commit is contained in:
NaiJi ✨ 2021-09-10 21:35:48 +03:00
parent 60d7b4e346
commit 46baf6fdfb
7 changed files with 81 additions and 45 deletions

15
include/tools/beatutils.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include <vector>
#include "mathutils.h"
namespace beat_utils
{
struct BeatInfo
{
long double rate_per_microsecond = 0.;
microsec average_interval = 0;
};
BeatInfo calculateBeatRateInfo(const std::vector<microsec>& approximate_deltas);
}

View File

@ -1,8 +1,8 @@
#pragma once
#include "tools/music.h"
#include "tools/beatutils.h"
#include <memory>
#include <vector>
class BPMCalculator
{
@ -15,15 +15,14 @@ public:
void start();
void stop();
void click();
float fetchCurrentBPMApproximation();
const beat_utils::BeatInfo& fetchApproximatedInfo();
microsec fetchTimeUntilNextBeat();
microsec getStartingOffset() const;
void setStartingOffset(microsec offset);
void moveStartingOffsetBy(microsec shift);
microsec fetchTimeUntilNextBeat();
microsec fetchBeatInterval();
private:
bool _need_recalculate;
bool _calculating;
@ -33,8 +32,7 @@ private:
microsec _previous_click_offset;
microsec _first_click_offset;
int _approximated_bpm;
beat_utils::BeatInfo _approximated_info;
inline float calculateBPM(microsec all_microseconds, std::size_t beats_amount) const;
inline void reset();
};

View File

@ -34,7 +34,7 @@ void Editor::enter()
{
auto& group = _group;
auto& music = _music;
_music->openFromFile("40mp.ogg");
_music->openFromFile("Uta-test.flac");
_music->setVolume(5);
_bpm_calculator = std::make_shared<BPMCalculator>(_music);

View File

@ -1,15 +1,21 @@
#include "bpmcalculatorwidget.h"
#include "tools/bpmcalculator.h"
#include <iostream>
BPMCalculatorWidget::BPMCalculatorWidget(const std::shared_ptr<BPMCalculator>& bpm_calculator, const std::shared_ptr<sf::Font>& font) :
Window("BPM Calculation", font),
_bpm_calculator(bpm_calculator),
_slider(std::make_shared<BPMSlider>())
_slider(std::make_shared<BPMSlider>()),
_ticked(false)
{
_bpm_value.setFont(*_font);
_bpm_value.setCharacterSize(40);
_bpm_value.setFillColor(sf::Color::Black);
_bpm_value.setString("--");
_slap_buffer.loadFromFile("Tick.ogg");
_slap.setBuffer(_slap_buffer);
_slap.setVolume(30.);
}
void BPMCalculatorWidget::input(const sf::Event& event)
@ -34,16 +40,22 @@ void BPMCalculatorWidget::update(const sf::Time& dt)
{
Window::update(dt);
const auto approximation = _bpm_calculator->fetchCurrentBPMApproximation();
if (approximation != 0)
const auto beat_info = _bpm_calculator->fetchApproximatedInfo();
if (beat_info.rate_per_microsecond != 0)
{
_bpm_value.setString(std::to_string(approximation));
_bpm_value.setString(std::to_string(static_cast<int>(beat_info.rate_per_microsecond)));
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;
const auto time_relation = static_cast<long double>(beat_info.average_interval) / static_cast<long double>(until_beat);
const auto slider_path_left = _slider->rect().width / time_relation;
if (slider_path_left < 50)
{
if (!_ticked)
_slap.play();
_ticked = true;
}
else
_ticked = false;
_slider->setTickPosition(slider_path_left);
}

View File

@ -6,6 +6,8 @@
#include <functional>
#include <SFML/Graphics/RectangleShape.hpp>
#include <SFML/Graphics/Text.hpp>
#include <SFML/Audio/Sound.hpp>
#include <SFML/Audio/SoundBuffer.hpp>
class BPMCalculator;
@ -28,6 +30,10 @@ private:
std::shared_ptr<BPMCalculator> _bpm_calculator;
std::shared_ptr<BPMSlider> _slider;
sf::SoundBuffer _slap_buffer;
sf::Sound _slap;
bool _ticked;
sf::Text _bpm_value;
};

23
src/tools/beatutils.cpp Normal file
View File

@ -0,0 +1,23 @@
#include "tools/beatutils.h"
#include <numeric>
#include <iostream>
auto beat_utils::calculateBeatRateInfo(const std::vector<microsec>& approximate_deltas) -> BeatInfo
{
if (approximate_deltas.empty())
return {};
const microsec sum = std::accumulate(approximate_deltas.begin(), approximate_deltas.end(), 0);
const std::size_t amount = approximate_deltas.size();
long double average = static_cast<long double>(sum)
/ static_cast<long double>(amount);
return BeatInfo
{
60000000. / average,
static_cast<microsec>(average)
};
}

View File

@ -3,8 +3,6 @@
#include <numeric>
#include <iostream>
constexpr float MICROSECONDS_IN_MINUTE_f = 60000000.;
BPMCalculator::BPMCalculator(const std::shared_ptr<Music>& music) :
_music(music)
{
@ -16,7 +14,7 @@ void BPMCalculator::reset()
_calculating = false;
_deltas.clear();
_previous_click_offset = 0;
_approximated_bpm = 0;
_first_click_offset = 0;
_need_recalculate = true;
}
@ -54,6 +52,7 @@ void BPMCalculator::click()
if (_previous_click_offset == 0)
{
_previous_click_offset = click_offset;
_first_click_offset = click_offset;
return;
}
@ -63,32 +62,20 @@ void BPMCalculator::click()
_previous_click_offset = click_offset;
}
float BPMCalculator::fetchCurrentBPMApproximation()
const beat_utils::BeatInfo& BPMCalculator::fetchApproximatedInfo()
{
if (!_need_recalculate)
return _approximated_bpm;
//if (!_need_recalculate)
//return _approximated_info;
_need_recalculate = false;
//_need_recalculate = false;
const microsec sum = std::accumulate(_deltas.begin(), _deltas.end(), 0);
bool hasEnoughDeltas = _deltas.size() >= 8;
_approximated_bpm = (!hasEnoughDeltas)
? 0.
: calculateBPM(sum, _deltas.size());
_approximated_info = (!hasEnoughDeltas)
? beat_utils::BeatInfo{}
: beat_utils::calculateBeatRateInfo(_deltas);
return _approximated_bpm;
}
float BPMCalculator::calculateBPM(microsec all_microseconds, std::size_t beats_amount) const
{
if (beats_amount == 0)
return 0;
float relation = static_cast<float>(all_microseconds)
/ static_cast<float>(beats_amount);
return static_cast<float>(1. / relation);
return _approximated_info;
}
microsec BPMCalculator::getStartingOffset() const
@ -110,10 +97,5 @@ microsec BPMCalculator::fetchTimeUntilNextBeat()
{
const microsec actual_offset = _music->fetchOffset() - _first_click_offset;
return fetchBeatInterval() % actual_offset;
}
microsec BPMCalculator::fetchBeatInterval()
{
return static_cast<microsec>(1. / fetchCurrentBPMApproximation());
return actual_offset % fetchApproximatedInfo().average_interval;
}