2022-03-16 02:10:18 +01:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <SFML/Config.hpp>
|
|
|
|
#include <algorithm>
|
|
|
|
#include <iterator>
|
|
|
|
#include <map>
|
2022-10-28 01:24:16 +02:00
|
|
|
#include <memory>
|
2022-04-06 15:47:04 +02:00
|
|
|
#include <set>
|
2022-03-16 02:10:18 +01:00
|
|
|
#include <sstream>
|
2022-10-28 01:24:16 +02:00
|
|
|
#include <type_traits>
|
2022-03-16 02:10:18 +01:00
|
|
|
#include <vector>
|
|
|
|
|
2022-04-14 01:26:31 +02:00
|
|
|
#include <json.hpp>
|
2022-03-16 02:10:18 +01:00
|
|
|
#include <SFML/System/Time.hpp>
|
|
|
|
|
|
|
|
#include "special_numeric_types.hpp"
|
|
|
|
|
|
|
|
namespace better {
|
|
|
|
struct SecondsAtBeat {
|
2022-04-07 00:14:01 +02:00
|
|
|
Decimal seconds;
|
2022-03-16 02:10:18 +01:00
|
|
|
Fraction beats;
|
|
|
|
};
|
|
|
|
|
|
|
|
class BPMAtBeat {
|
|
|
|
public:
|
2022-04-03 15:59:05 +02:00
|
|
|
BPMAtBeat(Decimal bpm, Fraction beats);
|
2022-03-31 03:50:15 +02:00
|
|
|
Decimal get_bpm() const;
|
2022-04-09 00:54:06 +02:00
|
|
|
double get_bpm_as_double() const;
|
2022-04-03 15:59:05 +02:00
|
|
|
Fraction get_beats() const;
|
2022-03-16 02:10:18 +01:00
|
|
|
private:
|
2022-03-31 03:50:15 +02:00
|
|
|
Decimal bpm;
|
2022-04-09 00:54:06 +02:00
|
|
|
double bpm_as_double;
|
2022-04-03 15:59:05 +02:00
|
|
|
Fraction beats;
|
2022-03-16 02:10:18 +01:00
|
|
|
};
|
|
|
|
|
2022-11-09 01:01:58 +01:00
|
|
|
class BPMEvent {
|
2022-03-16 02:10:18 +01:00
|
|
|
public:
|
2022-11-09 01:01:58 +01:00
|
|
|
BPMEvent(Fraction beats, double seconds, Decimal bpm);
|
2022-04-07 00:14:01 +02:00
|
|
|
Decimal get_bpm() const;
|
2022-04-09 00:54:06 +02:00
|
|
|
double get_bpm_as_double() const;
|
2022-04-07 00:14:01 +02:00
|
|
|
Fraction get_beats() const;
|
2022-04-09 00:54:06 +02:00
|
|
|
double get_seconds() const;
|
2022-04-14 00:13:38 +02:00
|
|
|
|
2022-11-09 01:01:58 +01:00
|
|
|
bool operator==(const BPMEvent&) const = default;
|
|
|
|
friend std::ostream& operator<<(std::ostream& out, const BPMEvent& b);
|
2022-03-16 02:10:18 +01:00
|
|
|
private:
|
2022-04-07 00:14:01 +02:00
|
|
|
Decimal bpm;
|
2022-04-09 00:54:06 +02:00
|
|
|
double bpm_as_double;
|
2022-04-07 00:14:01 +02:00
|
|
|
Fraction beats;
|
|
|
|
double seconds;
|
2022-03-16 02:10:18 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
class Timing {
|
|
|
|
public:
|
2022-10-19 00:50:34 +02:00
|
|
|
|
2022-11-09 01:01:58 +01:00
|
|
|
using bpm_event_type = BPMEvent;
|
|
|
|
using key_type = bpm_event_type;
|
2022-10-28 01:24:16 +02:00
|
|
|
|
2022-11-09 01:01:58 +01:00
|
|
|
struct beat_order_for_events {
|
2022-10-28 01:24:16 +02:00
|
|
|
template<class T>
|
|
|
|
bool operator()(const T& a, const T& b) const {
|
2022-11-09 01:01:58 +01:00
|
|
|
return a.get_beats() < b.get_beats();
|
2022-10-28 01:24:16 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-11-09 01:01:58 +01:00
|
|
|
using keys_by_beats_type = std::set<bpm_event_type, beat_order_for_events>;
|
2022-10-19 00:50:34 +02:00
|
|
|
|
2022-04-01 02:30:32 +02:00
|
|
|
Timing();
|
2022-04-07 00:14:01 +02:00
|
|
|
Timing(const std::vector<BPMAtBeat>& events, const Decimal& offset);
|
2022-03-16 02:10:18 +01:00
|
|
|
|
2022-04-07 00:14:01 +02:00
|
|
|
double seconds_at(Fraction beats) const;
|
2022-11-10 19:28:25 +01:00
|
|
|
private:
|
|
|
|
double seconds_without_offset_at(Fraction beats) const;
|
|
|
|
public:
|
2022-04-07 00:14:01 +02:00
|
|
|
double seconds_between(Fraction beat_a, Fraction beat_b) const;
|
2022-03-16 02:10:18 +01:00
|
|
|
sf::Time time_at(Fraction beats) const;
|
2022-03-17 02:50:30 +01:00
|
|
|
sf::Time time_between(Fraction beat_a, Fraction beat_b) const;
|
2022-03-16 02:10:18 +01:00
|
|
|
|
2022-03-17 02:50:30 +01:00
|
|
|
Fraction beats_at(sf::Time time) const;
|
2022-04-07 00:14:01 +02:00
|
|
|
Fraction beats_at(double seconds) const;
|
2022-03-31 03:50:15 +02:00
|
|
|
|
2022-10-19 00:50:34 +02:00
|
|
|
Decimal bpm_at(sf::Time time) const;
|
|
|
|
Decimal bpm_at(double seconds) const;
|
|
|
|
Decimal bpm_at(Fraction beats) const;
|
|
|
|
|
|
|
|
void insert(const BPMAtBeat& bpm_change);
|
2022-11-09 01:01:58 +01:00
|
|
|
void erase(const BPMAtBeat& bpm_change);
|
2022-10-19 00:50:34 +02:00
|
|
|
|
2022-11-10 00:48:13 +01:00
|
|
|
Decimal get_offset() const;
|
|
|
|
void set_offset(const Decimal& new_offset);
|
|
|
|
|
2022-04-02 04:10:09 +02:00
|
|
|
nlohmann::ordered_json dump_to_memon_1_0_0() const;
|
|
|
|
|
2022-04-03 15:59:05 +02:00
|
|
|
static Timing load_from_memon_1_0_0(const nlohmann::json& json);
|
|
|
|
static Timing load_from_memon_legacy(const nlohmann::json& metadata);
|
2023-07-08 15:58:11 +02:00
|
|
|
|
|
|
|
keys_by_beats_type::const_iterator cbegin() const;
|
|
|
|
keys_by_beats_type::const_iterator cend() const;
|
2022-10-27 22:08:56 +02:00
|
|
|
|
|
|
|
template<typename Callback>
|
|
|
|
void for_each_event_between(const Fraction& first, const Fraction& last, const Callback& cb) const {
|
2022-11-09 01:01:58 +01:00
|
|
|
const auto first_element = events_by_beats.lower_bound(bpm_event_type{first, 0, 1});
|
|
|
|
const auto last_element = events_by_beats.upper_bound(bpm_event_type{last, 0, 1});
|
|
|
|
std::for_each(first_element, last_element, [&](const bpm_event_type& ptr){cb(ptr);});
|
2022-10-27 22:08:56 +02:00
|
|
|
}
|
2023-03-17 01:02:38 +01:00
|
|
|
|
|
|
|
template<typename Callback>
|
|
|
|
void for_each_event_between(const sf::Time& start, const sf::Time& end, const Callback& cb) const {
|
|
|
|
for_each_event_between(
|
|
|
|
beats_at(start),
|
|
|
|
beats_at(end),
|
|
|
|
cb
|
|
|
|
);
|
|
|
|
}
|
2022-04-20 14:46:07 +02:00
|
|
|
|
2022-04-13 02:29:33 +02:00
|
|
|
bool operator==(const Timing&) const = default;
|
|
|
|
|
2022-04-16 02:26:37 +02:00
|
|
|
friend std::ostream& operator<<(std::ostream& out, const Timing& t);
|
|
|
|
friend fmt::formatter<better::Timing>;
|
2022-03-16 02:10:18 +01:00
|
|
|
private:
|
2022-11-10 00:48:13 +01:00
|
|
|
Decimal offset = 0;
|
|
|
|
double offset_as_double = 0;
|
2022-10-28 01:24:16 +02:00
|
|
|
|
2022-11-10 19:28:25 +01:00
|
|
|
// holds the bpm changes with seconds precomputed, seconds are synced
|
|
|
|
// as if beat zero was happening at zero seconds, ignoring any offset
|
2022-11-10 00:48:13 +01:00
|
|
|
keys_by_beats_type events_by_beats = {};
|
2022-11-10 19:28:25 +01:00
|
|
|
// holds a <seconds, beats> pair for each event to allow a quick search
|
|
|
|
// when querying by seconds, seconds held in this are synced as if beat
|
|
|
|
// zero was happenning at zero seconds
|
2022-11-10 00:48:13 +01:00
|
|
|
std::map<double, Fraction> seconds_to_beats = {};
|
|
|
|
|
|
|
|
void reconstruct(const std::vector<BPMAtBeat>& events, const Decimal& offset);
|
2022-10-19 00:50:34 +02:00
|
|
|
|
2022-11-10 19:28:25 +01:00
|
|
|
/* Reload using the given events */
|
2022-10-19 00:50:34 +02:00
|
|
|
void reload_events_from(const std::vector<BPMAtBeat>& events);
|
|
|
|
|
2022-11-10 00:48:13 +01:00
|
|
|
/* Shift all events in the timing object to make beat zero happen
|
|
|
|
at the given offset in seconds */
|
|
|
|
void shift_to_match(const Decimal& offset);
|
|
|
|
|
2022-10-28 01:24:16 +02:00
|
|
|
const key_type& bpm_event_in_effect_at(sf::Time time) const;
|
|
|
|
const key_type& bpm_event_in_effect_at(double seconds) const;
|
|
|
|
const key_type& bpm_event_in_effect_at(Fraction beats) const;
|
2022-03-16 02:10:18 +01:00
|
|
|
};
|
2022-04-14 01:26:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
template <>
|
2022-11-09 01:01:58 +01:00
|
|
|
struct fmt::formatter<better::BPMEvent>: formatter<string_view> {
|
2022-04-14 01:26:31 +02:00
|
|
|
// parse is inherited from formatter<string_view>.
|
|
|
|
template <typename FormatContext>
|
2022-11-09 01:01:58 +01:00
|
|
|
auto format(const better::BPMEvent& b, FormatContext& ctx) {
|
2022-04-14 01:26:31 +02:00
|
|
|
return format_to(
|
|
|
|
ctx.out(),
|
2022-04-16 02:26:37 +02:00
|
|
|
"BPMEvent(beats: {}, bpm: {})",
|
|
|
|
b.get_beats(),
|
|
|
|
b.get_bpm()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <>
|
|
|
|
struct fmt::formatter<better::Timing>: formatter<string_view> {
|
|
|
|
// parse is inherited from formatter<string_view>.
|
|
|
|
template <typename FormatContext>
|
|
|
|
auto format(const better::Timing& t, FormatContext& ctx) {
|
|
|
|
return format_to(
|
|
|
|
ctx.out(),
|
|
|
|
"Timing(offset: {}, events: [{}])",
|
|
|
|
t.offset,
|
|
|
|
fmt::join(t.events_by_beats, ", ")
|
2022-04-14 01:26:31 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
};
|