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
|
#pragma once
|
||||||
|
|
||||||
#include "tools/music.h"
|
#include "tools/music.h"
|
||||||
|
#include "tools/beatutils.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
class BPMCalculator
|
class BPMCalculator
|
||||||
{
|
{
|
||||||
|
@ -15,15 +15,14 @@ public:
|
||||||
void start();
|
void start();
|
||||||
void stop();
|
void stop();
|
||||||
void click();
|
void click();
|
||||||
float fetchCurrentBPMApproximation();
|
|
||||||
|
const beat_utils::BeatInfo& fetchApproximatedInfo();
|
||||||
|
microsec fetchTimeUntilNextBeat();
|
||||||
|
|
||||||
microsec getStartingOffset() const;
|
microsec getStartingOffset() const;
|
||||||
void setStartingOffset(microsec offset);
|
void setStartingOffset(microsec offset);
|
||||||
void moveStartingOffsetBy(microsec shift);
|
void moveStartingOffsetBy(microsec shift);
|
||||||
|
|
||||||
microsec fetchTimeUntilNextBeat();
|
|
||||||
microsec fetchBeatInterval();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _need_recalculate;
|
bool _need_recalculate;
|
||||||
bool _calculating;
|
bool _calculating;
|
||||||
|
@ -33,8 +32,7 @@ private:
|
||||||
microsec _previous_click_offset;
|
microsec _previous_click_offset;
|
||||||
microsec _first_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();
|
inline void reset();
|
||||||
};
|
};
|
||||||
|
|
|
@ -34,7 +34,7 @@ void Editor::enter()
|
||||||
{
|
{
|
||||||
auto& group = _group;
|
auto& group = _group;
|
||||||
auto& music = _music;
|
auto& music = _music;
|
||||||
_music->openFromFile("40mp.ogg");
|
_music->openFromFile("Uta-test.flac");
|
||||||
_music->setVolume(5);
|
_music->setVolume(5);
|
||||||
|
|
||||||
_bpm_calculator = std::make_shared<BPMCalculator>(_music);
|
_bpm_calculator = std::make_shared<BPMCalculator>(_music);
|
||||||
|
|
|
@ -1,15 +1,21 @@
|
||||||
#include "bpmcalculatorwidget.h"
|
#include "bpmcalculatorwidget.h"
|
||||||
#include "tools/bpmcalculator.h"
|
#include "tools/bpmcalculator.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
BPMCalculatorWidget::BPMCalculatorWidget(const std::shared_ptr<BPMCalculator>& bpm_calculator, const std::shared_ptr<sf::Font>& font) :
|
BPMCalculatorWidget::BPMCalculatorWidget(const std::shared_ptr<BPMCalculator>& bpm_calculator, const std::shared_ptr<sf::Font>& font) :
|
||||||
Window("BPM Calculation", font),
|
Window("BPM Calculation", font),
|
||||||
_bpm_calculator(bpm_calculator),
|
_bpm_calculator(bpm_calculator),
|
||||||
_slider(std::make_shared<BPMSlider>())
|
_slider(std::make_shared<BPMSlider>()),
|
||||||
|
_ticked(false)
|
||||||
{
|
{
|
||||||
_bpm_value.setFont(*_font);
|
_bpm_value.setFont(*_font);
|
||||||
_bpm_value.setCharacterSize(40);
|
_bpm_value.setCharacterSize(40);
|
||||||
_bpm_value.setFillColor(sf::Color::Black);
|
_bpm_value.setFillColor(sf::Color::Black);
|
||||||
_bpm_value.setString("--");
|
_bpm_value.setString("--");
|
||||||
|
_slap_buffer.loadFromFile("Tick.ogg");
|
||||||
|
_slap.setBuffer(_slap_buffer);
|
||||||
|
_slap.setVolume(30.);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BPMCalculatorWidget::input(const sf::Event& event)
|
void BPMCalculatorWidget::input(const sf::Event& event)
|
||||||
|
@ -34,16 +40,22 @@ void BPMCalculatorWidget::update(const sf::Time& dt)
|
||||||
{
|
{
|
||||||
Window::update(dt);
|
Window::update(dt);
|
||||||
|
|
||||||
const auto approximation = _bpm_calculator->fetchCurrentBPMApproximation();
|
const auto beat_info = _bpm_calculator->fetchApproximatedInfo();
|
||||||
if (approximation != 0)
|
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 until_beat = _bpm_calculator->fetchTimeUntilNextBeat();
|
||||||
const microsec beat_interval = _bpm_calculator->fetchBeatInterval();
|
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;
|
||||||
const auto time_relation = beat_interval / until_beat;
|
if (slider_path_left < 50)
|
||||||
const auto slider_path_left = Window::rect().width / time_relation;
|
{
|
||||||
|
if (!_ticked)
|
||||||
|
_slap.play();
|
||||||
|
_ticked = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_ticked = false;
|
||||||
|
|
||||||
_slider->setTickPosition(slider_path_left);
|
_slider->setTickPosition(slider_path_left);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <SFML/Graphics/RectangleShape.hpp>
|
#include <SFML/Graphics/RectangleShape.hpp>
|
||||||
#include <SFML/Graphics/Text.hpp>
|
#include <SFML/Graphics/Text.hpp>
|
||||||
|
#include <SFML/Audio/Sound.hpp>
|
||||||
|
#include <SFML/Audio/SoundBuffer.hpp>
|
||||||
|
|
||||||
class BPMCalculator;
|
class BPMCalculator;
|
||||||
|
|
||||||
|
@ -28,6 +30,10 @@ private:
|
||||||
std::shared_ptr<BPMCalculator> _bpm_calculator;
|
std::shared_ptr<BPMCalculator> _bpm_calculator;
|
||||||
std::shared_ptr<BPMSlider> _slider;
|
std::shared_ptr<BPMSlider> _slider;
|
||||||
|
|
||||||
|
sf::SoundBuffer _slap_buffer;
|
||||||
|
sf::Sound _slap;
|
||||||
|
bool _ticked;
|
||||||
|
|
||||||
sf::Text _bpm_value;
|
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 <numeric>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
constexpr float MICROSECONDS_IN_MINUTE_f = 60000000.;
|
|
||||||
|
|
||||||
BPMCalculator::BPMCalculator(const std::shared_ptr<Music>& music) :
|
BPMCalculator::BPMCalculator(const std::shared_ptr<Music>& music) :
|
||||||
_music(music)
|
_music(music)
|
||||||
{
|
{
|
||||||
|
@ -16,7 +14,7 @@ void BPMCalculator::reset()
|
||||||
_calculating = false;
|
_calculating = false;
|
||||||
_deltas.clear();
|
_deltas.clear();
|
||||||
_previous_click_offset = 0;
|
_previous_click_offset = 0;
|
||||||
_approximated_bpm = 0;
|
_first_click_offset = 0;
|
||||||
_need_recalculate = true;
|
_need_recalculate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,6 +52,7 @@ void BPMCalculator::click()
|
||||||
if (_previous_click_offset == 0)
|
if (_previous_click_offset == 0)
|
||||||
{
|
{
|
||||||
_previous_click_offset = click_offset;
|
_previous_click_offset = click_offset;
|
||||||
|
_first_click_offset = click_offset;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,32 +62,20 @@ void BPMCalculator::click()
|
||||||
_previous_click_offset = click_offset;
|
_previous_click_offset = click_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
float BPMCalculator::fetchCurrentBPMApproximation()
|
const beat_utils::BeatInfo& BPMCalculator::fetchApproximatedInfo()
|
||||||
{
|
{
|
||||||
if (!_need_recalculate)
|
//if (!_need_recalculate)
|
||||||
return _approximated_bpm;
|
//return _approximated_info;
|
||||||
|
|
||||||
_need_recalculate = false;
|
//_need_recalculate = false;
|
||||||
|
|
||||||
const microsec sum = std::accumulate(_deltas.begin(), _deltas.end(), 0);
|
|
||||||
bool hasEnoughDeltas = _deltas.size() >= 8;
|
bool hasEnoughDeltas = _deltas.size() >= 8;
|
||||||
|
|
||||||
_approximated_bpm = (!hasEnoughDeltas)
|
_approximated_info = (!hasEnoughDeltas)
|
||||||
? 0.
|
? beat_utils::BeatInfo{}
|
||||||
: calculateBPM(sum, _deltas.size());
|
: beat_utils::calculateBeatRateInfo(_deltas);
|
||||||
|
|
||||||
return _approximated_bpm;
|
return _approximated_info;
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
microsec BPMCalculator::getStartingOffset() const
|
microsec BPMCalculator::getStartingOffset() const
|
||||||
|
@ -110,10 +97,5 @@ microsec BPMCalculator::fetchTimeUntilNextBeat()
|
||||||
{
|
{
|
||||||
const microsec actual_offset = _music->fetchOffset() - _first_click_offset;
|
const microsec actual_offset = _music->fetchOffset() - _first_click_offset;
|
||||||
|
|
||||||
return fetchBeatInterval() % actual_offset;
|
return actual_offset % fetchApproximatedInfo().average_interval;
|
||||||
}
|
|
||||||
|
|
||||||
microsec BPMCalculator::fetchBeatInterval()
|
|
||||||
{
|
|
||||||
return static_cast<microsec>(1. / fetchCurrentBPMApproximation());
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue