mirror of
https://gitlab.com/square-game-liberation-front/F.E.I.S.git
synced 2025-02-23 13:39:02 +01:00
clangd is still absolutely sharting itself, I have to go the old-fashioned way and fix compiler errors one at a time ...
This commit is contained in:
parent
3ab51d5bb1
commit
32ea4b309b
@ -75,7 +75,12 @@ namespace better {
|
|||||||
"Attempted to create a LongNote with a zero-length tail"
|
"Attempted to create a LongNote with a zero-length tail"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (tail_tip.get_x() != position.get_x() and tail_tip.get_y() != position.get_y()) {
|
if (
|
||||||
|
not (
|
||||||
|
(tail_tip.get_x() == position.get_x())
|
||||||
|
xor (tail_tip.get_y() == position.get_y())
|
||||||
|
)
|
||||||
|
) {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "Attempted to create a LongNote with and invalid tail : ";
|
ss << "Attempted to create a LongNote with and invalid tail : ";
|
||||||
ss << "position: " << position << " , tail_tip: " << tail_tip;
|
ss << "position: " << position << " , tail_tip: " << tail_tip;
|
||||||
@ -174,6 +179,8 @@ namespace better {
|
|||||||
return {pos.get_x(), pos.get_y() + length};
|
return {pos.get_x(), pos.get_y() + length};
|
||||||
case 3: // left
|
case 3: // left
|
||||||
return {pos.get_x() - length, pos.get_y()};
|
return {pos.get_x() - length, pos.get_y()};
|
||||||
|
default:
|
||||||
|
return pos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "better_timing.hpp"
|
#include "better_timing.hpp"
|
||||||
|
|
||||||
|
#include <fmt/core.h>
|
||||||
#include <json.hpp>
|
#include <json.hpp>
|
||||||
|
|
||||||
#include "better_beats.hpp"
|
#include "better_beats.hpp"
|
||||||
@ -23,21 +24,28 @@ namespace better {
|
|||||||
return beats;
|
return beats;
|
||||||
}
|
}
|
||||||
|
|
||||||
BPMEvent::BPMEvent(Fraction beats, Fraction seconds, Decimal bpm) :
|
BPMEvent::BPMEvent(Fraction beats, double seconds, Decimal bpm) :
|
||||||
BPMAtBeat(bpm, beats),
|
bpm(bpm),
|
||||||
|
beats(beats),
|
||||||
seconds(seconds)
|
seconds(seconds)
|
||||||
{};
|
{};
|
||||||
|
|
||||||
|
Decimal BPMEvent::get_bpm() const {
|
||||||
|
return bpm;
|
||||||
|
}
|
||||||
|
|
||||||
|
Fraction BPMEvent::get_beats() const {
|
||||||
|
return beats;
|
||||||
|
}
|
||||||
|
|
||||||
Fraction BPMEvent::get_seconds() const {
|
Fraction BPMEvent::get_seconds() const {
|
||||||
return seconds;
|
return seconds;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
// Default constructor, used when creating a new song from scratch
|
||||||
Create a default-constructed Timing, which corresponds to the fallback
|
Timing::Timing()
|
||||||
timing object from the memon spec : 120 BPM, offset 0
|
Timing({120, 0}, 0)
|
||||||
*/
|
|
||||||
Timing::Timing():
|
|
||||||
Timing({{120, 0}}, {0,0})
|
|
||||||
{};
|
{};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -45,7 +53,9 @@ namespace better {
|
|||||||
beats, the offset parameter is more flexible than a "regular" beat zero
|
beats, the offset parameter is more flexible than a "regular" beat zero
|
||||||
offset as it accepts non-zero beats
|
offset as it accepts non-zero beats
|
||||||
*/
|
*/
|
||||||
Timing::Timing(const std::vector<BPMAtBeat>& events, const SecondsAtBeat& offset) {
|
Timing::Timing(const std::vector<BPMAtBeat>& events, const Decimal& new_offset) :
|
||||||
|
offset(new_offset)
|
||||||
|
{
|
||||||
if (events.empty()) {
|
if (events.empty()) {
|
||||||
throw std::invalid_argument(
|
throw std::invalid_argument(
|
||||||
"Attempted to create a Timing object with no BPM events"
|
"Attempted to create a Timing object with no BPM events"
|
||||||
@ -70,10 +80,8 @@ namespace better {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// First compute everything as if the first BPM change happened at
|
|
||||||
// zero seconds, then shift according to the offset
|
|
||||||
auto first_event = sorted_events.begin();
|
auto first_event = sorted_events.begin();
|
||||||
Fraction current_second = 0;
|
double current_second = 0;
|
||||||
std::vector<BPMEvent> bpm_changes;
|
std::vector<BPMEvent> bpm_changes;
|
||||||
bpm_changes.reserve(sorted_events.size());
|
bpm_changes.reserve(sorted_events.size());
|
||||||
bpm_changes.emplace_back(
|
bpm_changes.emplace_back(
|
||||||
@ -85,10 +93,11 @@ namespace better {
|
|||||||
auto previous = first_event;
|
auto previous = first_event;
|
||||||
auto current = std::next(first_event);
|
auto current = std::next(first_event);
|
||||||
for (; current != sorted_events.end(); ++previous, ++current) {
|
for (; current != sorted_events.end(); ++previous, ++current) {
|
||||||
auto beats_since_last_event =
|
Fraction beats_since_last_event =
|
||||||
current->get_beats() - previous->get_beats();
|
current->get_beats() - previous->get_beats();
|
||||||
auto seconds_since_last_event =
|
auto seconds_since_last_event =
|
||||||
(60 * beats_since_last_event) / convert_to_fraction(previous->get_bpm());
|
static_cast<double>(60 * beats_since_last_event)
|
||||||
|
/ static_cast<double>(previous->get_bpm());
|
||||||
current_second += seconds_since_last_event;
|
current_second += seconds_since_last_event;
|
||||||
bpm_changes.emplace_back(
|
bpm_changes.emplace_back(
|
||||||
current->get_beats(),
|
current->get_beats(),
|
||||||
@ -100,86 +109,67 @@ namespace better {
|
|||||||
.insert(bpm_changes.begin(), bpm_changes.end());
|
.insert(bpm_changes.begin(), bpm_changes.end());
|
||||||
this->events_by_seconds
|
this->events_by_seconds
|
||||||
.insert(bpm_changes.begin(), bpm_changes.end());
|
.insert(bpm_changes.begin(), bpm_changes.end());
|
||||||
auto unshifted_seconds_at_offset =
|
|
||||||
this->fractional_seconds_at(offset.beats);
|
|
||||||
auto shift = offset.seconds - unshifted_seconds_at_offset;
|
|
||||||
std::vector<BPMEvent> shifted_bpm_changes;
|
|
||||||
std::transform(
|
|
||||||
bpm_changes.begin(),
|
|
||||||
bpm_changes.end(),
|
|
||||||
std::back_inserter(shifted_bpm_changes),
|
|
||||||
[shift](const BPMEvent& b){
|
|
||||||
return BPMEvent(
|
|
||||||
b.get_beats(),
|
|
||||||
b.get_seconds() + shift,
|
|
||||||
b.get_bpm()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
this->events_by_beats
|
|
||||||
.insert(shifted_bpm_changes.begin(), shifted_bpm_changes.end());
|
|
||||||
this->events_by_seconds
|
|
||||||
.insert(shifted_bpm_changes.begin(), shifted_bpm_changes.end());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Return the number of seconds at the given beat.
|
Return the amount of seconds at the given beat.
|
||||||
|
|
||||||
Before the first bpm change, compute backwards from the first bpm,
|
Before the first bpm change, compute backwards from the first bpm,
|
||||||
after the first bpm change, compute forwards from the previous bpm
|
after the first bpm change, compute forwards from the previous bpm
|
||||||
change
|
change
|
||||||
*/
|
*/
|
||||||
Fraction Timing::fractional_seconds_at(Fraction beats) const {
|
double Timing::seconds_at(Fraction beats) const {
|
||||||
auto bpm_change = this->events_by_beats.upper_bound(BPMEvent(beats, 0, 0));
|
auto bpm_change = this->events_by_beats.upper_bound(BPMEvent(beats, 0, 0));
|
||||||
if (bpm_change != this->events_by_beats.begin()) {
|
if (bpm_change != this->events_by_beats.begin()) {
|
||||||
bpm_change = std::prev(bpm_change);
|
bpm_change = std::prev(bpm_change);
|
||||||
}
|
}
|
||||||
auto beats_since_previous_event = beats - bpm_change->get_beats();
|
auto beats_since_previous_event = beats - bpm_change->get_beats();
|
||||||
auto seconds_since_previous_event = (
|
auto seconds_since_previous_event = (
|
||||||
Fraction{60}
|
static_cast<double>(60 * beats_since_previous_event)
|
||||||
* beats_since_previous_event
|
/ static_cast<double>(bpm_change->get_bpm())
|
||||||
/ convert_to_fraction(bpm_change->get_bpm())
|
);
|
||||||
|
return (
|
||||||
|
static_cast<double>(offset)
|
||||||
|
+ bpm_change->get_seconds()
|
||||||
|
+ seconds_since_previous_event
|
||||||
);
|
);
|
||||||
return bpm_change->get_seconds() + seconds_since_previous_event;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Fraction Timing::fractional_seconds_between(
|
double Timing::seconds_between(Fraction beat_a, Fraction beat_b) const {
|
||||||
Fraction beat_a,
|
return seconds_at(beat_b) - seconds_at(beat_a);
|
||||||
Fraction beat_b
|
|
||||||
) const {
|
|
||||||
return (
|
|
||||||
fractional_seconds_at(beat_b)
|
|
||||||
- fractional_seconds_at(beat_a)
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
sf::Time Timing::time_at(Fraction beats) const {
|
sf::Time Timing::time_at(Fraction beats) const {
|
||||||
return frac_to_time(fractional_seconds_at(beats));
|
return sf::seconds(seconds_at(beats));
|
||||||
};
|
};
|
||||||
|
|
||||||
sf::Time Timing::time_between(Fraction beat_a, Fraction beat_b) const {
|
sf::Time Timing::time_between(Fraction beat_a, Fraction beat_b) const {
|
||||||
return frac_to_time(fractional_seconds_between(beat_a, beat_b));
|
return sf::seconds(seconds_between(beat_a, beat_b));
|
||||||
};
|
};
|
||||||
|
|
||||||
Fraction Timing::beats_at(sf::Time time) const {
|
Fraction Timing::beats_at(sf::Time time) const {
|
||||||
Fraction fractional_seconds{convert_to_mpz(static_cast<std::uint64_t>(time.asMicroseconds())), 1000000};
|
const auto seconds = static_cast<double>(time.asMicroseconds()) / 1000000.0;
|
||||||
auto bpm_change = this->events_by_seconds.upper_bound(BPMEvent(0, fractional_seconds, 0));
|
return beats_at(seconds);
|
||||||
|
};
|
||||||
|
|
||||||
|
Fraction Timing::beats_at(double seconds) const {
|
||||||
|
seconds -= static_cast<double>(offset);
|
||||||
|
auto bpm_change = this->events_by_seconds.upper_bound(BPMEvent(0, seconds, 0));
|
||||||
if (bpm_change != this->events_by_seconds.begin()) {
|
if (bpm_change != this->events_by_seconds.begin()) {
|
||||||
bpm_change = std::prev(bpm_change);
|
bpm_change = std::prev(bpm_change);
|
||||||
}
|
}
|
||||||
auto seconds_since_previous_event = fractional_seconds - bpm_change->get_seconds();
|
auto seconds_since_previous_event = seconds - bpm_change->get_seconds();
|
||||||
auto beats_since_previous_event = (
|
auto beats_since_previous_event = (
|
||||||
convert_to_fraction(bpm_change->get_bpm())
|
convert_to_fraction(bpm_change->get_bpm())
|
||||||
* seconds_since_previous_event
|
* Fraction{seconds_since_previous_event}
|
||||||
/ Fraction{60}
|
/ 60
|
||||||
);
|
);
|
||||||
return bpm_change->get_beats() + beats_since_previous_event;
|
return bpm_change->get_beats() + beats_since_previous_event;
|
||||||
};
|
};
|
||||||
|
|
||||||
nlohmann::ordered_json Timing::dump_to_memon_1_0_0() const {
|
nlohmann::ordered_json Timing::dump_to_memon_1_0_0() const {
|
||||||
nlohmann::ordered_json j;
|
nlohmann::ordered_json j;
|
||||||
const auto offset = fractional_seconds_at(0);
|
j["offset"] = offset.format("f");
|
||||||
j["offset"] = convert_to_decimal(offset, 5).format("f");
|
|
||||||
auto bpms = nlohmann::ordered_json::array();
|
auto bpms = nlohmann::ordered_json::array();
|
||||||
for (const auto& bpm_change : events_by_beats) {
|
for (const auto& bpm_change : events_by_beats) {
|
||||||
bpms.push_back({
|
bpms.push_back({
|
||||||
@ -189,14 +179,13 @@ namespace better {
|
|||||||
}
|
}
|
||||||
j["bpms"] = bpms;
|
j["bpms"] = bpms;
|
||||||
return j;
|
return j;
|
||||||
}
|
};
|
||||||
|
|
||||||
Timing Timing::load_from_memon_1_0_0(const nlohmann::json& json) {
|
Timing Timing::load_from_memon_1_0_0(const nlohmann::json& json) {
|
||||||
Fraction offset = 0;
|
double offset = 0;
|
||||||
if (json.contains("offset")) {
|
if (json.contains("offset")) {
|
||||||
const auto string_offset = json["offset"].get<std::string>();
|
const auto string_offset = json["offset"].get<std::string>();
|
||||||
const auto decimal_offset = Decimal{string_offset};
|
offset = Decimal{string_offset};
|
||||||
offset = convert_to_fraction(decimal_offset);
|
|
||||||
}
|
}
|
||||||
std::uint64_t resolution = 240;
|
std::uint64_t resolution = 240;
|
||||||
if (json.contains("resolution")) {
|
if (json.contains("resolution")) {
|
||||||
@ -219,25 +208,17 @@ namespace better {
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
bpms,
|
bpms,
|
||||||
{offset, 0}
|
offset
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
For this function, offset is the OPPOSITE of the time (in seconds) at which
|
In legacy memon, offset is the OPPOSITE of the time (in seconds) at which
|
||||||
the first beat occurs in the music file
|
the first beat occurs in the music file
|
||||||
*/
|
*/
|
||||||
Timing Timing::load_from_memon_legacy(const nlohmann::json& metadata) {
|
Timing Timing::load_from_memon_legacy(const nlohmann::json& metadata) {
|
||||||
const auto bpm = Decimal{metadata["BPM"].get<std::string>()};
|
const auto bpm = Decimal{metadata["BPM"].get<std::string>()};
|
||||||
const auto offset = convert_to_fraction(Decimal{metadata["offset"].get<std::string>()});
|
const auto offset = Decimal{metadata["offset"].get<std::string>()};
|
||||||
const BPMAtBeat bpm_at_beat{bpm, 0};
|
return Timing{{bpm, 0}, -1 * offset};
|
||||||
const SecondsAtBeat seconds_at_beat{-1 * offset, 0};
|
|
||||||
return Timing{{bpm_at_beat}, seconds_at_beat};
|
|
||||||
}
|
|
||||||
|
|
||||||
sf::Time frac_to_time(const Fraction& f) {
|
|
||||||
const Fraction microseconds = f * 1000000;
|
|
||||||
const mpz_class approximated = microseconds.get_num() / microseconds.get_den();
|
|
||||||
return sf::microseconds(convert_to_int64(approximated));
|
|
||||||
};
|
};
|
||||||
}
|
}
|
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
namespace better {
|
namespace better {
|
||||||
struct SecondsAtBeat {
|
struct SecondsAtBeat {
|
||||||
Fraction seconds;
|
Decimal seconds;
|
||||||
Fraction beats;
|
Fraction beats;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -29,12 +29,16 @@ namespace better {
|
|||||||
Fraction beats;
|
Fraction beats;
|
||||||
};
|
};
|
||||||
|
|
||||||
class BPMEvent : public BPMAtBeat {
|
class BPMEvent {
|
||||||
public:
|
public:
|
||||||
BPMEvent(Fraction beats, Fraction seconds, Decimal bpm);
|
BPMEvent(Fraction beats, double seconds, Decimal bpm);
|
||||||
|
Decimal get_bpm() const;
|
||||||
|
Fraction get_beats() const;
|
||||||
Fraction get_seconds() const;
|
Fraction get_seconds() const;
|
||||||
private:
|
private:
|
||||||
Fraction seconds;
|
Decimal bpm;
|
||||||
|
Fraction beats;
|
||||||
|
double seconds;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct OrderByBeats {
|
struct OrderByBeats {
|
||||||
@ -54,24 +58,25 @@ namespace better {
|
|||||||
class Timing {
|
class Timing {
|
||||||
public:
|
public:
|
||||||
Timing();
|
Timing();
|
||||||
Timing(const std::vector<BPMAtBeat>& events, const SecondsAtBeat& offset);
|
Timing(const std::vector<BPMAtBeat>& events, const Decimal& offset);
|
||||||
|
|
||||||
Fraction fractional_seconds_at(Fraction beats) const;
|
double seconds_at(Fraction beats) const;
|
||||||
Fraction fractional_seconds_between(Fraction beat_a, Fraction beat_b) const;
|
double seconds_between(Fraction beat_a, Fraction beat_b) const;
|
||||||
sf::Time time_at(Fraction beats) const;
|
sf::Time time_at(Fraction beats) const;
|
||||||
sf::Time time_between(Fraction beat_a, Fraction beat_b) const;
|
sf::Time time_between(Fraction beat_a, Fraction beat_b) const;
|
||||||
|
|
||||||
Fraction beats_at(sf::Time time) const;
|
Fraction beats_at(sf::Time time) const;
|
||||||
|
Fraction beats_at(double seconds) const;
|
||||||
|
|
||||||
nlohmann::ordered_json dump_to_memon_1_0_0() const;
|
nlohmann::ordered_json dump_to_memon_1_0_0() const;
|
||||||
|
|
||||||
static Timing load_from_memon_1_0_0(const nlohmann::json& json);
|
static Timing load_from_memon_1_0_0(const nlohmann::json& json);
|
||||||
static Timing load_from_memon_legacy(const nlohmann::json& metadata);
|
static Timing load_from_memon_legacy(const nlohmann::json& metadata);
|
||||||
|
|
||||||
|
Decimal offset;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::set<BPMEvent, OrderByBeats> events_by_beats;
|
std::set<BPMEvent, OrderByBeats> events_by_beats;
|
||||||
std::set<BPMEvent, OrderBySeconds> events_by_seconds;
|
std::set<BPMEvent, OrderBySeconds> events_by_seconds;
|
||||||
};
|
};
|
||||||
|
|
||||||
sf::Time frac_to_time(const Fraction& f);
|
|
||||||
}
|
}
|
@ -58,6 +58,44 @@ EditorState::EditorState(
|
|||||||
reload_jacket();
|
reload_jacket();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int EditorState::get_volume() const {
|
||||||
|
return volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorState::set_volume(int newMusicVolume) {
|
||||||
|
volume = std::clamp(newMusicVolume, 0, 10);
|
||||||
|
if (music) {
|
||||||
|
music->setVolume(Toolbox::convertVolumeToNormalizedDB(volume)*100.f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorState::volume_up() {
|
||||||
|
set_volume(volume + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorState::volume_down() {
|
||||||
|
set_volume(volume - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int EditorState::get_speed() const {
|
||||||
|
return speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorState::set_speed(int newMusicSpeed) {
|
||||||
|
speed = std::clamp(newMusicSpeed, 1, 20);
|
||||||
|
if (music) {
|
||||||
|
music->setPitch(speed / 10.f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorState::speed_up() {
|
||||||
|
set_speed(speed + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorState::speed_down() {
|
||||||
|
set_speed(speed - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const Interval<sf::Time>& EditorState::get_editable_range() {
|
const Interval<sf::Time>& EditorState::get_editable_range() {
|
||||||
reload_editable_range();
|
reload_editable_range();
|
||||||
@ -68,14 +106,14 @@ void EditorState::set_playback_position(sf::Time newPosition) {
|
|||||||
newPosition = std::clamp(newPosition, editable_range.start, editable_range.end);
|
newPosition = std::clamp(newPosition, editable_range.start, editable_range.end);
|
||||||
previous_playback_position = newPosition - (sf::seconds(1) / 60.f);
|
previous_playback_position = newPosition - (sf::seconds(1) / 60.f);
|
||||||
playback_position = newPosition;
|
playback_position = newPosition;
|
||||||
if (music_state) {
|
if (music) {
|
||||||
if (
|
if (
|
||||||
playback_position >= sf::Time::Zero
|
playback_position >= sf::Time::Zero
|
||||||
and playback_position < music_state->music.getDuration()
|
and playback_position < music->getDuration()
|
||||||
) {
|
) {
|
||||||
music_state->music.setPlayingOffset(playback_position);
|
music->setPlayingOffset(playback_position);
|
||||||
} else {
|
} else {
|
||||||
music_state->music.stop();
|
music->stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -187,7 +225,12 @@ void EditorState::display_playfield(Marker& marker, MarkerEndingState markerEndi
|
|||||||
(ImVec4) ImColor::HSV(0, 0, 1.f, 0.5f));
|
(ImVec4) ImColor::HSV(0, 0, 1.f, 0.5f));
|
||||||
if (ImGui::ImageButton(playfield.button, {squareSize, squareSize}, 0)) {
|
if (ImGui::ImageButton(playfield.button, {squareSize, squareSize}, 0)) {
|
||||||
if (chart_state) {
|
if (chart_state) {
|
||||||
chart_state->toggle_note(playback_position, snap, {x, y});
|
chart_state->toggle_note(
|
||||||
|
playback_position,
|
||||||
|
snap,
|
||||||
|
{x, y},
|
||||||
|
applicable_timing
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ImGui::IsItemHovered() and chart_state and chart_state->creating_long_note) {
|
if (ImGui::IsItemHovered() and chart_state and chart_state->creating_long_note) {
|
||||||
@ -262,7 +305,7 @@ void EditorState::display_properties() {
|
|||||||
if (feis::InputTextColored(
|
if (feis::InputTextColored(
|
||||||
"Audio",
|
"Audio",
|
||||||
&song.metadata.audio,
|
&song.metadata.audio,
|
||||||
music_state.has_value(),
|
music.has_value(),
|
||||||
"Invalid Audio Path"
|
"Invalid Audio Path"
|
||||||
)) {
|
)) {
|
||||||
reload_music();
|
reload_music();
|
||||||
@ -295,9 +338,9 @@ void EditorState::display_properties() {
|
|||||||
Decimal{0},
|
Decimal{0},
|
||||||
song.metadata.preview_loop.start
|
song.metadata.preview_loop.start
|
||||||
);
|
);
|
||||||
if (music_state.has_value()) {
|
if (music.has_value()) {
|
||||||
song.metadata.preview_loop.start = std::min(
|
song.metadata.preview_loop.start = std::min(
|
||||||
Decimal{music_state->music.getDuration().asMicroseconds()} / 1000000,
|
Decimal{music->getDuration().asMicroseconds()} / 1000000,
|
||||||
song.metadata.preview_loop.start
|
song.metadata.preview_loop.start
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -307,12 +350,12 @@ void EditorState::display_properties() {
|
|||||||
Decimal{0},
|
Decimal{0},
|
||||||
song.metadata.preview_loop.duration
|
song.metadata.preview_loop.duration
|
||||||
);
|
);
|
||||||
if (music_state.has_value()) {
|
if (music.has_value()) {
|
||||||
song.metadata.preview_loop.start = std::min(
|
song.metadata.preview_loop.start = std::min(
|
||||||
(
|
(
|
||||||
Decimal{
|
Decimal{
|
||||||
music_state->music
|
music
|
||||||
.getDuration()
|
->getDuration()
|
||||||
.asMicroseconds()
|
.asMicroseconds()
|
||||||
} / 1000000
|
} / 1000000
|
||||||
- song.metadata.preview_loop.start
|
- song.metadata.preview_loop.start
|
||||||
@ -335,7 +378,7 @@ status of the editor. Will appear in the "Editor Status" window
|
|||||||
void EditorState::display_status() {
|
void EditorState::display_status() {
|
||||||
ImGui::Begin("Status", &showStatus, ImGuiWindowFlags_AlwaysAutoResize);
|
ImGui::Begin("Status", &showStatus, ImGuiWindowFlags_AlwaysAutoResize);
|
||||||
{
|
{
|
||||||
if (not music_state) {
|
if (not music) {
|
||||||
if (not song.metadata.audio.empty()) {
|
if (not song.metadata.audio.empty()) {
|
||||||
ImGui::TextColored(
|
ImGui::TextColored(
|
||||||
ImVec4(1, 0.42, 0.41, 1),
|
ImVec4(1, 0.42, 0.41, 1),
|
||||||
@ -396,12 +439,12 @@ void EditorState::display_playback_status() {
|
|||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::TextDisabled("Beats :");
|
ImGui::TextDisabled("Beats :");
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::TextUnformatted(convert_to_decimal(this->current_exact_beats(), 3).format(".3f").c_str());
|
ImGui::TextUnformatted(fmt::format("{:.3f}", current_exact_beats().get_d()).c_str());
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (music_state) {
|
if (music) {
|
||||||
ImGui::TextDisabled("Music File Offset :");
|
ImGui::TextDisabled("Music File Offset :");
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::TextUnformatted(Toolbox::to_string(music_state->music.getPrecisePlayingOffset()).c_str());
|
ImGui::TextUnformatted(Toolbox::to_string(music->getPrecisePlayingOffset()).c_str());
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
}
|
}
|
||||||
ImGui::TextDisabled("Timeline Position :");
|
ImGui::TextDisabled("Timeline Position :");
|
||||||
@ -524,6 +567,7 @@ void EditorState::display_linear_view() {
|
|||||||
if (chart_state) {
|
if (chart_state) {
|
||||||
linear_view.update(
|
linear_view.update(
|
||||||
*chart_state,
|
*chart_state,
|
||||||
|
applicable_timing,
|
||||||
playback_position,
|
playback_position,
|
||||||
ImGui::GetContentRegionMax()
|
ImGui::GetContentRegionMax()
|
||||||
);
|
);
|
||||||
@ -589,7 +633,7 @@ EditorState::SaveOutcome EditorState::ask_to_save_if_needed() {
|
|||||||
}
|
}
|
||||||
case EditorState::UserWantsToSave::No:
|
case EditorState::UserWantsToSave::No:
|
||||||
return EditorState::SaveOutcome::UserDeclindedSaving;
|
return EditorState::SaveOutcome::UserDeclindedSaving;
|
||||||
case EditorState::UserWantsToSave::Cancel:
|
default:
|
||||||
return EditorState::SaveOutcome::UserCanceled;
|
return EditorState::SaveOutcome::UserCanceled;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -662,12 +706,21 @@ void EditorState::open_chart(const std::string& name) {
|
|||||||
reload_applicable_timing();
|
reload_applicable_timing();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void EditorState::update_visible_notes() {
|
||||||
|
if (chart_state) {
|
||||||
|
chart_state->update_visible_notes(
|
||||||
|
playback_position,
|
||||||
|
applicable_timing
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
void EditorState::reload_editable_range() {
|
void EditorState::reload_editable_range() {
|
||||||
auto old_range = this->editable_range;
|
auto old_range = this->editable_range;
|
||||||
Interval<sf::Time> new_range;
|
Interval<sf::Time> new_range;
|
||||||
if (music_state) {
|
if (music) {
|
||||||
new_range += music_state->music.getDuration();
|
new_range += music->getDuration();
|
||||||
}
|
}
|
||||||
if (chart_state and not chart_state->chart.notes.empty()) {
|
if (chart_state and not chart_state->chart.notes.empty()) {
|
||||||
const auto beat_of_last_event = chart_state->chart.notes.crbegin()->second.get_end();
|
const auto beat_of_last_event = chart_state->chart.notes.crbegin()->second.get_end();
|
||||||
@ -677,7 +730,7 @@ void EditorState::reload_editable_range() {
|
|||||||
new_range.end += sf::seconds(10);
|
new_range.end += sf::seconds(10);
|
||||||
|
|
||||||
// If there is no music, make sure we can edit at least the first whole minute
|
// If there is no music, make sure we can edit at least the first whole minute
|
||||||
if (not music_state) {
|
if (not music) {
|
||||||
new_range += sf::seconds(60);
|
new_range += sf::seconds(60);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -715,15 +768,15 @@ void EditorState::reload_jacket() {
|
|||||||
*/
|
*/
|
||||||
void EditorState::reload_music() {
|
void EditorState::reload_music() {
|
||||||
if (not song_path.has_value() or song.metadata.audio.empty()) {
|
if (not song_path.has_value() or song.metadata.audio.empty()) {
|
||||||
music_state.reset();
|
music.reset();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto absolute_music_path = song_path->parent_path() / song.metadata.audio;
|
const auto absolute_music_path = song_path->parent_path() / song.metadata.audio;
|
||||||
try {
|
try {
|
||||||
music_state.emplace(absolute_music_path);
|
music.emplace(absolute_music_path);
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
music_state.reset();
|
music.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
reload_editable_range();
|
reload_editable_range();
|
||||||
@ -778,7 +831,7 @@ void EditorState::save(const std::filesystem::path& path) {
|
|||||||
|
|
||||||
void feis::open(std::optional<EditorState>& ed, std::filesystem::path assets, std::filesystem::path settings) {
|
void feis::open(std::optional<EditorState>& ed, std::filesystem::path assets, std::filesystem::path settings) {
|
||||||
if (ed and ed->ask_to_save_if_needed() == EditorState::SaveOutcome::UserCanceled) {
|
if (ed and ed->ask_to_save_if_needed() == EditorState::SaveOutcome::UserCanceled) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* _filepath = tinyfd_openFileDialog(
|
const char* _filepath = tinyfd_openFileDialog(
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
#include "history.hpp"
|
#include "history.hpp"
|
||||||
#include "history_actions.hpp"
|
#include "history_actions.hpp"
|
||||||
#include "marker.hpp"
|
#include "marker.hpp"
|
||||||
#include "music_state.hpp"
|
|
||||||
#include "notes_clipboard.hpp"
|
#include "notes_clipboard.hpp"
|
||||||
#include "notifications_queue.hpp"
|
#include "notifications_queue.hpp"
|
||||||
#include "playfield.hpp"
|
#include "playfield.hpp"
|
||||||
@ -39,7 +38,18 @@ public:
|
|||||||
|
|
||||||
std::optional<ChartState> chart_state;
|
std::optional<ChartState> chart_state;
|
||||||
|
|
||||||
std::optional<MusicState> music_state;
|
std::optional<PreciseMusic> music;
|
||||||
|
|
||||||
|
int get_volume() const;
|
||||||
|
void set_volume(int newMusicVolume);
|
||||||
|
void volume_up();
|
||||||
|
void volume_down();
|
||||||
|
|
||||||
|
/* These speed dials also work when no music is loaded */
|
||||||
|
int get_speed() const;
|
||||||
|
void set_speed(int newMusicSpeed);
|
||||||
|
void speed_up();
|
||||||
|
void speed_down();
|
||||||
|
|
||||||
std::optional<sf::Music> preview_audio;
|
std::optional<sf::Music> preview_audio;
|
||||||
|
|
||||||
@ -100,7 +110,7 @@ public:
|
|||||||
|
|
||||||
SaveOutcome ask_to_save_if_needed();
|
SaveOutcome ask_to_save_if_needed();
|
||||||
|
|
||||||
void save_if_needed();
|
SaveOutcome save_if_needed();
|
||||||
|
|
||||||
bool needs_to_save() const;
|
bool needs_to_save() const;
|
||||||
|
|
||||||
@ -131,8 +141,13 @@ public:
|
|||||||
|
|
||||||
void open_chart(const std::string& name);
|
void open_chart(const std::string& name);
|
||||||
|
|
||||||
|
void update_visible_notes();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
int volume = 10; // 0 -> 10
|
||||||
|
int speed = 10; // 1 -> 20
|
||||||
|
|
||||||
/*
|
/*
|
||||||
sf::Time bounds (in the audio file "coordinates") which are accessible
|
sf::Time bounds (in the audio file "coordinates") which are accessible
|
||||||
(and maybe editable) from the editor, can extend before and after
|
(and maybe editable) from the editor, can extend before and after
|
||||||
|
@ -91,12 +91,12 @@ public:
|
|||||||
if (next_actions.empty()) {
|
if (next_actions.empty()) {
|
||||||
last_saved_action = nullptr;
|
last_saved_action = nullptr;
|
||||||
} else {
|
} else {
|
||||||
last_saved_action = previous_actions.front();
|
last_saved_action = &previous_actions.front();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool current_state_is_saved() const {
|
bool current_state_is_saved() const {
|
||||||
return last_saved_action == previous_actions.front();
|
return last_saved_action == &previous_actions.front();
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -11,7 +11,7 @@ LNMarker::LNMarker(std::filesystem::path folder) :
|
|||||||
square_background(load_tex_with_prefix<16, 200>(folder, "LN0001_M")),
|
square_background(load_tex_with_prefix<16, 200>(folder, "LN0001_M")),
|
||||||
tail_cycle(load_tex_with_prefix<16, 0>(folder, "LN0001_M"))
|
tail_cycle(load_tex_with_prefix<16, 0>(folder, "LN0001_M"))
|
||||||
{
|
{
|
||||||
for (tex& : tail_cycle) {
|
for (auto& tex : tail_cycle) {
|
||||||
tex.setRepeated(true);
|
tex.setRepeated(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ private:
|
|||||||
template<std::size_t number, unsigned int first = 0>
|
template<std::size_t number, unsigned int first = 0>
|
||||||
std::array<sf::Texture, number> load_tex_with_prefix(
|
std::array<sf::Texture, number> load_tex_with_prefix(
|
||||||
const std::filesystem::path& folder,
|
const std::filesystem::path& folder,
|
||||||
const std::string& prefix,
|
const std::string& prefix
|
||||||
) {
|
) {
|
||||||
std::array<sf::Texture, number> res;
|
std::array<sf::Texture, number> res;
|
||||||
for (unsigned int frame = first; frame <= first + number - 1; frame++) {
|
for (unsigned int frame = first; frame <= first + number - 1; frame++) {
|
||||||
@ -52,17 +52,15 @@ std::array<sf::Texture, number> load_tex_with_prefix(
|
|||||||
"{prefix}{frame:03}.png",
|
"{prefix}{frame:03}.png",
|
||||||
fmt::arg("prefix", prefix),
|
fmt::arg("prefix", prefix),
|
||||||
fmt::arg("frame", frame)
|
fmt::arg("frame", frame)
|
||||||
)
|
);
|
||||||
std::stringstream filename;
|
std::filesystem::path texFile = folder / filename;
|
||||||
filename << prefix << std::setfill('0') << std::setw(3)
|
|
||||||
<< frame << ".png";
|
|
||||||
std::filesystem::path texFile = folder / filename.str();
|
|
||||||
sf::Texture tex;
|
sf::Texture tex;
|
||||||
if (!tex.loadFromFile(texFile.string())) {
|
if (!tex.loadFromFile(texFile.string())) {
|
||||||
std::stringstream err;
|
throw std::runtime_error(fmt::format(
|
||||||
err << "Unable to load texture folder " << folder
|
"Unable to load texture folder {}, failed on texture {}",
|
||||||
<< "\nfailed on texture " << filename.str();
|
folder.string(),
|
||||||
throw std::runtime_error(err.str());
|
filename
|
||||||
|
));
|
||||||
}
|
}
|
||||||
tex.setSmooth(true);
|
tex.setSmooth(true);
|
||||||
res.at(frame - first) = tex;
|
res.at(frame - first) = tex;
|
||||||
|
53
src/main.cpp
53
src/main.cpp
@ -196,12 +196,12 @@ int main() {
|
|||||||
// Arrow keys
|
// Arrow keys
|
||||||
case sf::Keyboard::Up:
|
case sf::Keyboard::Up:
|
||||||
if (event.key.shift) {
|
if (event.key.shift) {
|
||||||
if (editor_state and editor_state->music_state) {
|
if (editor_state) {
|
||||||
editor_state->music_state->volume_up();
|
editor_state->volume_up();
|
||||||
notificationsQueue.push(
|
notificationsQueue.push(
|
||||||
std::make_shared<TextNotification>(fmt::format(
|
std::make_shared<TextNotification>(fmt::format(
|
||||||
"Music Volume : {}%",
|
"Music Volume : {}%",
|
||||||
editor_state->music_state->volume * 10
|
editor_state->get_volume() * 10
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -213,12 +213,12 @@ int main() {
|
|||||||
break;
|
break;
|
||||||
case sf::Keyboard::Down:
|
case sf::Keyboard::Down:
|
||||||
if (event.key.shift) {
|
if (event.key.shift) {
|
||||||
if (editor_state and editor_state->music_state) {
|
if (editor_state) {
|
||||||
editor_state->music_state->volume_down();
|
editor_state->volume_down();
|
||||||
notificationsQueue.push(
|
notificationsQueue.push(
|
||||||
std::make_shared<TextNotification>(fmt::format(
|
std::make_shared<TextNotification>(fmt::format(
|
||||||
"Music Volume : {}%",
|
"Music Volume : {}%",
|
||||||
editor_state->music_state->volume * 10
|
editor_state->get_volume() * 10
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -230,11 +230,11 @@ int main() {
|
|||||||
break;
|
break;
|
||||||
case sf::Keyboard::Left:
|
case sf::Keyboard::Left:
|
||||||
if (event.key.shift) {
|
if (event.key.shift) {
|
||||||
if (editor_state and editor_state->music_state) {
|
if (editor_state) {
|
||||||
editor_state->music_state->speed_down();
|
editor_state->speed_down();
|
||||||
notificationsQueue.push(std::make_shared<TextNotification>(fmt::format(
|
notificationsQueue.push(std::make_shared<TextNotification>(fmt::format(
|
||||||
"Speed : {}%",
|
"Speed : {}%",
|
||||||
editor_state->music_state->speed * 10
|
editor_state->get_speed() * 10
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -251,11 +251,11 @@ int main() {
|
|||||||
break;
|
break;
|
||||||
case sf::Keyboard::Right:
|
case sf::Keyboard::Right:
|
||||||
if (event.key.shift) {
|
if (event.key.shift) {
|
||||||
if (editor_state and editor_state->music_state) {
|
if (editor_state) {
|
||||||
editor_state->music_state->speed_up();
|
editor_state->speed_up();
|
||||||
notificationsQueue.push(std::make_shared<TextNotification>(fmt::format(
|
notificationsQueue.push(std::make_shared<TextNotification>(fmt::format(
|
||||||
"Speed : {}%",
|
"Speed : {}%",
|
||||||
editor_state->music_state->speed * 10
|
editor_state->get_speed() * 10
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -397,28 +397,23 @@ int main() {
|
|||||||
|
|
||||||
// Audio playback management
|
// Audio playback management
|
||||||
if (editor_state) {
|
if (editor_state) {
|
||||||
if (editor_state->chart_state) {
|
editor_state->update_visible_notes();
|
||||||
editor_state->chart_state->update_visible_notes(
|
|
||||||
editor_state->playback_position,
|
|
||||||
editor_state->applicable_timing
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (editor_state->playing) {
|
if (editor_state->playing) {
|
||||||
editor_state->previous_playback_position = editor_state->playback_position;
|
editor_state->previous_playback_position = editor_state->playback_position;
|
||||||
editor_state->playback_position += delta * (editor_state->musicSpeed / 10.f);
|
editor_state->playback_position += delta * (editor_state->get_speed() / 10.f);
|
||||||
if (editor_state->music) {
|
if (editor_state->music) {
|
||||||
switch (editor_state->music->getStatus()) {
|
switch (editor_state->music->getStatus()) {
|
||||||
case sf::Music::Stopped:
|
case sf::Music::Stopped:
|
||||||
case sf::Music::Paused:
|
case sf::Music::Paused:
|
||||||
if (editor_state->playbackPosition.asSeconds() >= 0
|
if (editor_state->playback_position.asSeconds() >= 0
|
||||||
and editor_state->playbackPosition
|
and editor_state->playback_position
|
||||||
< editor_state->music->getDuration()) {
|
< editor_state->music->getDuration()) {
|
||||||
editor_state->music->setPlayingOffset(editor_state->playbackPosition);
|
editor_state->music->setPlayingOffset(editor_state->playback_position);
|
||||||
editor_state->music->play();
|
editor_state->music->play();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case sf::Music::Playing:
|
case sf::Music::Playing:
|
||||||
editor_state->playbackPosition =
|
editor_state->playback_position =
|
||||||
editor_state->music->getPrecisePlayingOffset();
|
editor_state->music->getPrecisePlayingOffset();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -427,9 +422,9 @@ int main() {
|
|||||||
}
|
}
|
||||||
if (beatTick.shouldPlay) {
|
if (beatTick.shouldPlay) {
|
||||||
auto previous_tick = static_cast<int>(editor_state->ticks_at(
|
auto previous_tick = static_cast<int>(editor_state->ticks_at(
|
||||||
editor_state->previousPos.asSeconds()));
|
editor_state->previous_playback_position.asSeconds()));
|
||||||
auto current_tick = static_cast<int>(editor_state->ticks_at(
|
auto current_tick = static_cast<int>(editor_state->ticks_at(
|
||||||
editor_state->playbackPosition.asSeconds()));
|
editor_state->playback_position.asSeconds()));
|
||||||
if (previous_tick / editor_state->getResolution()
|
if (previous_tick / editor_state->getResolution()
|
||||||
!= current_tick / editor_state->getResolution()) {
|
!= current_tick / editor_state->getResolution()) {
|
||||||
beatTick.play();
|
beatTick.play();
|
||||||
@ -439,8 +434,8 @@ int main() {
|
|||||||
int note_count = 0;
|
int note_count = 0;
|
||||||
for (auto note : editor_state->visibleNotes) {
|
for (auto note : editor_state->visibleNotes) {
|
||||||
float noteTiming = editor_state->getSecondsAt(note.getTiming());
|
float noteTiming = editor_state->getSecondsAt(note.getTiming());
|
||||||
if (noteTiming >= editor_state->previousPos.asSeconds()
|
if (noteTiming >= editor_state->previous_playback_position.asSeconds()
|
||||||
and noteTiming <= editor_state->playbackPosition.asSeconds()) {
|
and noteTiming <= editor_state->playback_position.asSeconds()) {
|
||||||
note_count++;
|
note_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -455,9 +450,9 @@ int main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (editor_state->playbackPosition > editor_state->getPreviewEnd()) {
|
if (editor_state->playback_position > editor_state->getPreviewEnd()) {
|
||||||
editor_state->playing = false;
|
editor_state->playing = false;
|
||||||
editor_state->playbackPosition = editor_state->getPreviewEnd();
|
editor_state->playback_position = editor_state->getPreviewEnd();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (editor_state->music) {
|
if (editor_state->music) {
|
||||||
|
@ -16,7 +16,6 @@ sources += files(
|
|||||||
'ln_marker.cpp',
|
'ln_marker.cpp',
|
||||||
'main.cpp',
|
'main.cpp',
|
||||||
'marker.cpp',
|
'marker.cpp',
|
||||||
'music_state.cpp',
|
|
||||||
'note.cpp',
|
'note.cpp',
|
||||||
'notes_clipboard.cpp',
|
'notes_clipboard.cpp',
|
||||||
'notification.cpp',
|
'notification.cpp',
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
#include "music_state.hpp"
|
|
||||||
#include "toolbox.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
void MusicState::set_speed(int newMusicSpeed) {
|
|
||||||
speed = std::clamp(newMusicSpeed, 1, 20);
|
|
||||||
music.setPitch(speed / 10.f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MusicState::speed_up() {
|
|
||||||
set_speed(speed + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MusicState::speed_down() {
|
|
||||||
set_speed(speed - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MusicState::set_volume(int newMusicVolume) {
|
|
||||||
volume = std::clamp(newMusicVolume, 0, 10);
|
|
||||||
music.setVolume(Toolbox::convertVolumeToNormalizedDB(volume)*100.f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MusicState::volume_up() {
|
|
||||||
set_volume(volume + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MusicState::volume_down() {
|
|
||||||
set_volume(volume - 1);
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <filesystem>
|
|
||||||
|
|
||||||
#include "precise_music.hpp"
|
|
||||||
|
|
||||||
struct MusicState {
|
|
||||||
explicit MusicState(const std::filesystem::path& path) : music(path) {};
|
|
||||||
PreciseMusic music;
|
|
||||||
int volume = 10; // 0 -> 10
|
|
||||||
void set_volume(int newMusicVolume);
|
|
||||||
void volume_up();
|
|
||||||
void volume_down();
|
|
||||||
|
|
||||||
int speed = 10; // 1 -> 20
|
|
||||||
void set_speed(int newMusicSpeed);
|
|
||||||
void speed_up();
|
|
||||||
void speed_down();
|
|
||||||
};
|
|
@ -7,54 +7,54 @@
|
|||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
#include <SFML/Graphics/RenderTexture.hpp>
|
#include <SFML/Graphics/RenderTexture.hpp>
|
||||||
|
|
||||||
// #include "better_note.hpp"
|
#include "better_note.hpp"
|
||||||
// #include "better_timing.hpp"
|
#include "better_timing.hpp"
|
||||||
// #include "ln_marker.hpp"
|
#include "ln_marker.hpp"
|
||||||
// #include "marker.hpp"
|
#include "marker.hpp"
|
||||||
|
|
||||||
struct LongNote {
|
|
||||||
// template<typename ...Ts>
|
|
||||||
// LongNote(Ts&&... Args) : marker(std::forward<Ts>(Args)...) {};
|
|
||||||
|
|
||||||
LNMarker marker;
|
|
||||||
sf::RenderTexture layer;
|
|
||||||
sf::Sprite backgroud;
|
|
||||||
sf::Sprite outline;
|
|
||||||
sf::Sprite highlight;
|
|
||||||
sf::Sprite tail;
|
|
||||||
sf::Sprite triangle;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Playfield {
|
class Playfield {
|
||||||
// public:
|
public:
|
||||||
// Playfield(std::filesystem::path assets_folder);
|
Playfield(std::filesystem::path assets_folder);
|
||||||
// sf::Texture base_texture;
|
sf::Texture base_texture;
|
||||||
// sf::Sprite button;
|
sf::Sprite button;
|
||||||
// sf::Sprite button_pressed;
|
sf::Sprite button_pressed;
|
||||||
// sf::Sprite note_selected;
|
sf::Sprite note_selected;
|
||||||
// sf::Sprite note_collision;
|
sf::Sprite note_collision;
|
||||||
|
|
||||||
// sf::RenderTexture marker_layer;
|
sf::RenderTexture marker_layer;
|
||||||
// sf::Sprite marker_sprite;
|
sf::Sprite marker_sprite;
|
||||||
|
|
||||||
// LongNote long_note;
|
struct LongNote {
|
||||||
|
template<typename ...Ts>
|
||||||
|
LongNote(Ts&&... Args) : marker(std::forward<Ts>(Args)...) {};
|
||||||
|
|
||||||
// void resize(unsigned int width);
|
LNMarker marker;
|
||||||
|
sf::RenderTexture layer;
|
||||||
|
sf::Sprite backgroud;
|
||||||
|
sf::Sprite outline;
|
||||||
|
sf::Sprite highlight;
|
||||||
|
sf::Sprite tail;
|
||||||
|
sf::Sprite triangle;
|
||||||
|
};
|
||||||
|
|
||||||
|
LongNote long_note;
|
||||||
|
|
||||||
// void draw_tail_and_receptor(
|
void resize(unsigned int width);
|
||||||
// const better::LongNote& note,
|
|
||||||
// const sf::Time& playbackPosition,
|
|
||||||
// const better::Timing& timing
|
|
||||||
// );
|
|
||||||
|
|
||||||
// void draw_long_note(
|
void draw_tail_and_receptor(
|
||||||
// const better::LongNote& note,
|
const better::LongNote& note,
|
||||||
// const sf::Time& playbackPosition,
|
const sf::Time& playbackPosition,
|
||||||
// const better::Timing& timing,
|
const better::Timing& timing
|
||||||
// Marker& marker,
|
);
|
||||||
// MarkerEndingState& markerEndingState
|
|
||||||
// );
|
|
||||||
|
|
||||||
// private:
|
void draw_long_note(
|
||||||
// const std::filesystem::path texture_path;
|
const better::LongNote& note,
|
||||||
|
const sf::Time& playbackPosition,
|
||||||
|
const better::Timing& timing,
|
||||||
|
Marker& marker,
|
||||||
|
MarkerEndingState& markerEndingState
|
||||||
|
);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::filesystem::path texture_path;
|
||||||
};
|
};
|
||||||
|
@ -30,15 +30,6 @@ Fraction round_fraction(const Fraction& f) {
|
|||||||
return floor_fraction(f + Fraction{1, 2});
|
return floor_fraction(f + Fraction{1, 2});
|
||||||
};
|
};
|
||||||
|
|
||||||
Decimal convert_to_decimal(const Fraction& f, std::uint64_t precision) {
|
|
||||||
const Fraction precision_mod = fast_pow(Fraction{1, 10}, precision);
|
|
||||||
const Fraction floored = f - (f % precision_mod);
|
|
||||||
return (
|
|
||||||
Decimal{convert_to_int64(floored.get_num())}
|
|
||||||
/ Decimal{convert_to_int64(floored.get_den())}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
Fraction convert_to_fraction(const Decimal& d) {
|
Fraction convert_to_fraction(const Decimal& d) {
|
||||||
const auto reduced = d.reduce();
|
const auto reduced = d.reduce();
|
||||||
const auto sign = reduced.sign();
|
const auto sign = reduced.sign();
|
||||||
@ -47,7 +38,7 @@ Fraction convert_to_fraction(const Decimal& d) {
|
|||||||
if (exponent >= 0) {
|
if (exponent >= 0) {
|
||||||
return Fraction{sign > 0 ? 1 : -1} * Fraction{coefficient} * fast_pow(Fraction{10}, exponent);
|
return Fraction{sign > 0 ? 1 : -1} * Fraction{coefficient} * fast_pow(Fraction{10}, exponent);
|
||||||
} else {
|
} else {
|
||||||
return Fraction{sign > 0 ? 1 : -1} * Fraction{coefficient} / fast_pow(Fraction{10}, exponent);
|
return Fraction{sign > 0 ? 1 : -1} * Fraction{coefficient} / fast_pow(Fraction{10}, -exponent);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -61,37 +52,4 @@ Fraction floor_beats(Fraction beats, std::uint64_t denominator) {
|
|||||||
beats *= denominator;
|
beats *= denominator;
|
||||||
const auto nearest = floor_fraction(beats);
|
const auto nearest = floor_fraction(beats);
|
||||||
return nearest / Fraction{denominator};
|
return nearest / Fraction{denominator};
|
||||||
};
|
|
||||||
|
|
||||||
mpz_class convert_to_mpz(std::uint64_t u) {
|
|
||||||
auto low = static_cast<std::uint32_t>(u);
|
|
||||||
auto high = static_cast<std::uint32_t>(u >> 32);
|
|
||||||
return mpz_class{low} + (mpz_class{high} << 32);
|
|
||||||
};
|
|
||||||
|
|
||||||
mpz_class convert_to_mpz(std::int64_t i) {
|
|
||||||
if (i < 0) {
|
|
||||||
return -1 * convert_to_mpz(static_cast<std::uint64_t>(-1 * i));
|
|
||||||
} else {
|
|
||||||
return convert_to_mpz(static_cast<std::uint64_t>(i));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::uint64_t convert_to_uint64(const mpz_class& m) {
|
|
||||||
if (m < 0) {
|
|
||||||
throw std::invalid_argument("Cannot convert negative mpz to std::uint64_t");
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto low = static_cast<std::uint64_t>(m.get_ui());
|
|
||||||
const mpz_class high_m = m >> 32;
|
|
||||||
const auto high = static_cast<std::uint64_t>(high_m.get_ui());
|
|
||||||
return low + (high << 32);
|
|
||||||
};
|
|
||||||
|
|
||||||
std::int64_t convert_to_int64(const mpz_class& m) {
|
|
||||||
if (m < 0) {
|
|
||||||
return -1 * static_cast<std::int64_t>(convert_to_uint64(-1 * m));
|
|
||||||
} else {
|
|
||||||
return static_cast<std::int64_t>(convert_to_uint64(m));
|
|
||||||
}
|
|
||||||
};
|
};
|
@ -13,7 +13,6 @@ std::strong_ordering operator<=>(const Fraction& lhs, const Fraction& rhs);
|
|||||||
Fraction operator%(Fraction a, const Fraction& b);
|
Fraction operator%(Fraction a, const Fraction& b);
|
||||||
Fraction floor_fraction(const Fraction& f);
|
Fraction floor_fraction(const Fraction& f);
|
||||||
Fraction round_fraction(const Fraction& f);
|
Fraction round_fraction(const Fraction& f);
|
||||||
Decimal convert_to_decimal(const Fraction& f, std::uint64_t precision);
|
|
||||||
Fraction convert_to_fraction(const Decimal& d);
|
Fraction convert_to_fraction(const Decimal& d);
|
||||||
|
|
||||||
// Rounds a given beat to the nearest given division (defaults to nearest 1/240th)
|
// Rounds a given beat to the nearest given division (defaults to nearest 1/240th)
|
||||||
@ -35,9 +34,4 @@ Number fast_pow(const Number base, const std::uint64_t exponent) {
|
|||||||
n /= 2;
|
n /= 2;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
mpz_class convert_to_mpz(std::uint64_t u);
|
|
||||||
mpz_class convert_to_mpz(std::int64_t i);
|
|
||||||
std::uint64_t convert_to_uint64(const mpz_class& m);
|
|
||||||
std::int64_t convert_to_int64(const mpz_class& m);
|
|
@ -1,11 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
template<class A, class B>
|
template<class A, class B>
|
||||||
B apply_or(std::optional<A> opt, B(*func)(A), B b) {
|
B apply_or(std::optional<A> opt, std::function<B(A)> func, B b) {
|
||||||
if (opt.has_value()) {
|
if (opt.has_value()) {
|
||||||
return func(*opt);
|
return func(*opt);
|
||||||
} else {
|
} else {
|
||||||
@ -15,14 +16,13 @@ B apply_or(std::optional<A> opt, B(*func)(A), B b) {
|
|||||||
|
|
||||||
template<class A>
|
template<class A>
|
||||||
std::string stringify_or(std::optional<A> opt, std::string fallback) {
|
std::string stringify_or(std::optional<A> opt, std::string fallback) {
|
||||||
auto cb = [](const A& a){
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << a;
|
|
||||||
return ss.str();
|
|
||||||
};
|
|
||||||
return apply_or(
|
return apply_or(
|
||||||
opt,
|
opt,
|
||||||
&cb,
|
[](const A& a) -> std::string {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << a;
|
||||||
|
return ss.str();
|
||||||
|
},
|
||||||
fallback
|
fallback
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -54,7 +54,7 @@ void LinearView::resize(unsigned int width, unsigned int height) {
|
|||||||
|
|
||||||
void LinearView::update(
|
void LinearView::update(
|
||||||
const ChartState& chart_state,
|
const ChartState& chart_state,
|
||||||
const Timing& timing,
|
const better::Timing& timing,
|
||||||
const sf::Time& playback_position,
|
const sf::Time& playback_position,
|
||||||
const ImVec2& size
|
const ImVec2& size
|
||||||
) {
|
) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user