forked from NaiJi/project-kyoku
Implement slider tick animation on BPM calculator
This commit is contained in:
parent
60d7b4e346
commit
46baf6fdfb
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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)
|
||||
};
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue