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:
Stepland 2022-04-07 00:14:01 +02:00
parent 3ab51d5bb1
commit 32ea4b309b
17 changed files with 258 additions and 301 deletions

View File

@ -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;
} }
} }

View File

@ -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));
}; };
} }

View File

@ -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);
} }

View File

@ -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(

View File

@ -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

View File

@ -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:

View File

@ -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);
} }
} }

View File

@ -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;

View File

@ -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) {

View File

@ -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',

View File

@ -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);
}

View File

@ -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();
};

View File

@ -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;
}; };

View File

@ -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));
}
}; };

View File

@ -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);

View File

@ -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
); );
} }

View File

@ -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
) { ) {