mirror of
https://gitlab.com/square-game-liberation-front/F.E.I.S.git
synced 2024-09-24 03:28:28 +02:00
async marker loading !
This commit is contained in:
parent
0446e717ec
commit
f190201582
@ -36,6 +36,7 @@
|
||||
- The editable time range of a chart now grows in a way that should interfere less with editing
|
||||
- Support for the jujube marker format
|
||||
- Sound parameters are saved
|
||||
- Markers and Marker previews are loaded in the background, which should avoid long boot up times + UI freezes
|
||||
|
||||
## 🚧 Changes 🚧
|
||||
- Force using the asset folder next to the executable
|
||||
|
@ -24,8 +24,12 @@
|
||||
#include <SFML/System/Vector2.hpp>
|
||||
#include <tinyfiledialogs.h>
|
||||
|
||||
#include "custom_sfml_audio/synced_sound_streams.hpp"
|
||||
#include "widgets/linear_view.hpp"
|
||||
#include "better_metadata.hpp"
|
||||
#include "better_note.hpp"
|
||||
#include "better_song.hpp"
|
||||
#include "better_timing.hpp"
|
||||
#include "chart_state.hpp"
|
||||
#include "compile_time_info.hpp"
|
||||
#include "file_dialogs.hpp"
|
||||
@ -36,14 +40,10 @@
|
||||
#include "long_note_dummy.hpp"
|
||||
#include "notifications_queue.hpp"
|
||||
#include "special_numeric_types.hpp"
|
||||
#include "better_metadata.hpp"
|
||||
#include "better_timing.hpp"
|
||||
#include "custom_sfml_audio/synced_sound_streams.hpp"
|
||||
#include "utf8_sfml_redefinitions.hpp"
|
||||
#include "variant_visitor.hpp"
|
||||
#include "utf8_strings.hpp"
|
||||
#include "variant_visitor.hpp"
|
||||
#include "waveform.hpp"
|
||||
#include "widgets/linear_view.hpp"
|
||||
|
||||
EditorState::EditorState(const std::filesystem::path& assets_, config::Config& config_) :
|
||||
config(config_),
|
||||
@ -434,7 +434,7 @@ Fraction EditorState::get_snap_step() const {
|
||||
return Fraction{1, snap};
|
||||
};
|
||||
|
||||
void EditorState::display_playfield(const std::optional<std::shared_ptr<Marker>>& opt_marker, Judgement markerEndingState) {
|
||||
void EditorState::display_playfield(const Markers::marker_type& opt_marker, Judgement markerEndingState) {
|
||||
ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_Once);
|
||||
ImGui::SetNextWindowSizeConstraints(
|
||||
ImVec2(0, 0),
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "generic_interval.hpp"
|
||||
#include "history.hpp"
|
||||
#include "marker.hpp"
|
||||
#include "markers.hpp"
|
||||
#include "clipboard.hpp"
|
||||
#include "notifications_queue.hpp"
|
||||
#include "playfield.hpp"
|
||||
@ -142,7 +143,7 @@ public:
|
||||
Fraction get_snap_step() const;
|
||||
|
||||
bool show_playfield = true;
|
||||
void display_playfield(const std::optional<std::shared_ptr<Marker>>& marker, Judgement markerEndingState);
|
||||
void display_playfield(const Markers::marker_type& marker, Judgement markerEndingState);
|
||||
|
||||
bool show_file_properties = false;
|
||||
void display_file_properties();
|
||||
|
89
src/main.cpp
89
src/main.cpp
@ -4,6 +4,7 @@
|
||||
#include <SFML/Graphics/Texture.hpp>
|
||||
#include <SFML/System/Time.hpp>
|
||||
#include <SFML/Window/Keyboard.hpp>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <exception>
|
||||
#include <filesystem>
|
||||
@ -28,6 +29,7 @@
|
||||
#include "history_item.hpp"
|
||||
#include "imgui_extras.hpp"
|
||||
#include "marker.hpp"
|
||||
#include "markers.hpp"
|
||||
#include "mp3_reader.hpp"
|
||||
#include "notifications_queue.hpp"
|
||||
#include "src/custom_sfml_audio/synced_sound_streams.hpp"
|
||||
@ -36,8 +38,6 @@
|
||||
#include "widgets/blank_screen.hpp"
|
||||
|
||||
int main() {
|
||||
// TODO : Make the playfield not appear when there's no chart selected
|
||||
|
||||
// extend SFML to be able to read mp3's
|
||||
sf::SoundFileFactory::registerReader<sf::priv::SoundFileReaderMp3>();
|
||||
|
||||
@ -78,49 +78,7 @@ int main() {
|
||||
|
||||
IO.ConfigWindowsMoveFromTitleBarOnly = true;
|
||||
|
||||
// Loading markers preview
|
||||
std::map<std::filesystem::path, std::future<std::optional<feis::Texture>>> marker_previews_loaders;
|
||||
std::map<std::filesystem::path, std::optional<feis::Texture>> marker_previews;
|
||||
for (const auto& dir_entry : std::filesystem::directory_iterator(markers_folder)) {
|
||||
const auto folder = std::filesystem::relative(dir_entry, markers_folder);
|
||||
marker_previews_loaders[folder] = std::async(
|
||||
std::launch::async,
|
||||
[](const std::filesystem::path& folder) -> std::optional<feis::Texture> {
|
||||
for (const auto& file : {"preview.png", "ma15.png"}) {
|
||||
const auto preview_path = folder / file;
|
||||
if (std::filesystem::exists(preview_path)) {
|
||||
feis::Texture preview;
|
||||
if (preview.load_from_path(preview_path)) {
|
||||
return preview;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
},
|
||||
dir_entry.path()
|
||||
);
|
||||
}
|
||||
|
||||
using MarkerTuple = std::tuple<std::filesystem::path, std::optional<std::shared_ptr<Marker>>>;
|
||||
std::future<MarkerTuple> marker_loader;
|
||||
const auto load_marker_async = [&](const std::filesystem::path& folder) -> MarkerTuple {
|
||||
try {
|
||||
return {folder, load_marker_from(markers_folder / folder)};
|
||||
} catch (const std::exception& e) {
|
||||
return {folder, {}};
|
||||
}
|
||||
};
|
||||
std::shared_ptr<Marker> marker = [&](const std::optional<std::filesystem::path>& folder){
|
||||
if (folder and folder->is_relative()) {
|
||||
try {
|
||||
return load_marker_from(markers_folder / *folder);
|
||||
} catch (const std::exception& e) {
|
||||
fmt::print("Failed to load marker from preferences");
|
||||
}
|
||||
}
|
||||
return first_available_marker_in(markers_folder);
|
||||
}(config.marker.folder);
|
||||
|
||||
Markers markers{markers_folder, config};
|
||||
if (not config.marker.ending_state) {
|
||||
config.marker.ending_state = Judgement::Perfect;
|
||||
}
|
||||
@ -490,7 +448,7 @@ int main() {
|
||||
editor_state->display_history();
|
||||
}
|
||||
if (editor_state->chart_state and editor_state->show_playfield) {
|
||||
editor_state->display_playfield(marker, markerEndingState);
|
||||
editor_state->display_playfield(markers.get_chosen_marker(), markerEndingState);
|
||||
}
|
||||
if (editor_state->show_linear_view) {
|
||||
editor_state->display_linear_view();
|
||||
@ -781,33 +739,44 @@ int main() {
|
||||
}
|
||||
if (ImGui::BeginMenu("Marker")) {
|
||||
int i = 0;
|
||||
for (const auto& [path, opt_preview] : marker_previews) {
|
||||
std::for_each(
|
||||
markers.cbegin(),
|
||||
markers.cend(),
|
||||
[&](const auto& it){
|
||||
const auto& [path, opt_preview] = it;
|
||||
ImGui::PushID(path.c_str());
|
||||
bool clicked = false;
|
||||
if (opt_preview) {
|
||||
if (ImGui::ImageButton(*opt_preview, {100, 100})) {
|
||||
|
||||
marker_loader = std::async
|
||||
marker = marker_ptr;
|
||||
config.marker.folder = path;
|
||||
clicked = ImGui::ImageButton(*opt_preview, {100, 100});
|
||||
} else {
|
||||
clicked = ImGui::Button(path_to_utf8_encoded_string(path).c_str(), {100, 100});
|
||||
}
|
||||
if (clicked) {
|
||||
markers.load_marker(path);
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
i++;
|
||||
if (i % 4 != 0) {
|
||||
ImGui::SameLine();
|
||||
}
|
||||
}
|
||||
);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("Marker Ending State")) {
|
||||
for (const auto& [judgement, name] : judgement_to_name) {
|
||||
const auto preview = marker->judgement_preview(judgement);
|
||||
if (const auto& marker = markers.get_chosen_marker()) {
|
||||
const auto preview = (*marker)->judgement_preview(judgement);
|
||||
if (ImGui::ImageButton(preview, {100, 100})) {
|
||||
markerEndingState = judgement;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::TextUnformatted(name.c_str());
|
||||
} else {
|
||||
if (ImGui::Button((name+"##Marker Ending State").c_str())) {
|
||||
markerEndingState = judgement;
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
@ -824,19 +793,9 @@ int main() {
|
||||
}
|
||||
}
|
||||
ImGui::EndMainMenuBar();
|
||||
|
||||
ImGui::SFML::Render(window);
|
||||
window.display();
|
||||
|
||||
for (auto& [path, future] : marker_previews_loaders) {
|
||||
if (future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
|
||||
marker_previews[path] = future.get();
|
||||
}
|
||||
}
|
||||
for (auto& future : )
|
||||
if (marker_loader.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
|
||||
marker = marker_loader.get();
|
||||
}
|
||||
markers.update();
|
||||
}
|
||||
|
||||
ImGui::SFML::Shutdown();
|
||||
|
@ -196,8 +196,8 @@ std::shared_ptr<Marker> load_marker_from(const std::filesystem::path& folder) {
|
||||
return std::make_shared<JujubeMarker>(std::move(JujubeMarker::load_from_folder(folder)));
|
||||
}
|
||||
|
||||
std::shared_ptr<Marker> first_available_marker_in(const std::filesystem::path& assets_folder) {
|
||||
for (auto& folder : std::filesystem::directory_iterator(assets_folder / "textures" / "markers")) {
|
||||
std::shared_ptr<Marker> first_available_marker_in(const std::filesystem::path& markers_folder) {
|
||||
for (auto& folder : std::filesystem::directory_iterator(markers_folder)) {
|
||||
try {
|
||||
return load_marker_from(folder);
|
||||
} catch (const std::exception&) {}
|
||||
|
164
src/markers.cpp
Normal file
164
src/markers.cpp
Normal file
@ -0,0 +1,164 @@
|
||||
#include "markers.hpp"
|
||||
|
||||
#include <future>
|
||||
#include <ranges>
|
||||
|
||||
#include "fmt/core.h"
|
||||
#include "imgui.h"
|
||||
|
||||
#include "marker.hpp"
|
||||
#include "utf8_strings.hpp"
|
||||
|
||||
Markers::Markers(
|
||||
const std::filesystem::path& markers_folder_,
|
||||
config::Config& config_
|
||||
) :
|
||||
markers_folder(markers_folder_),
|
||||
config(config_)
|
||||
{
|
||||
|
||||
auto contents = std::filesystem::directory_iterator(markers_folder);
|
||||
auto subdirs = std::views::filter(contents, [](const auto& dir_it){return dir_it.is_directory();});
|
||||
|
||||
for (const auto& dir_it : subdirs) {
|
||||
const auto folder = std::filesystem::relative(dir_it, markers_folder);
|
||||
preview_loaders[folder] = std::async(std::launch::async, load_marker_preview, dir_it.path());
|
||||
}
|
||||
|
||||
marker_loader = std::make_tuple(
|
||||
std::filesystem::path{},
|
||||
std::async(std::launch::async, load_default_marker, std::cref(config.marker.folder), std::cref(markers_folder))
|
||||
);
|
||||
}
|
||||
|
||||
void Markers::load_marker(const std::filesystem::path& chosen) {
|
||||
auto& [path, future] = marker_loader;
|
||||
if (future.valid()) {
|
||||
discarded_markers.emplace_back(std::async(std::launch::async, discard_marker, std::move(future)));
|
||||
}
|
||||
marker_loader = std::make_tuple(
|
||||
chosen,
|
||||
std::async(std::launch::async, load_marker_async, std::cref(chosen), std::cref(markers_folder))
|
||||
);
|
||||
}
|
||||
|
||||
const Markers::marker_type& Markers::get_chosen_marker() const {
|
||||
return chosen_marker;
|
||||
}
|
||||
|
||||
Markers::previews_type::const_iterator Markers::cbegin() const {
|
||||
return previews.cbegin();
|
||||
}
|
||||
|
||||
Markers::previews_type::const_iterator Markers::cend() const {
|
||||
return previews.cend();
|
||||
}
|
||||
|
||||
void Markers::update() {
|
||||
std::erase_if(preview_loaders, [](const auto& it){
|
||||
return not it.second.valid();
|
||||
});
|
||||
for (auto& [path, future] : preview_loaders) {
|
||||
if (future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
|
||||
previews[path] = future.get();
|
||||
}
|
||||
}
|
||||
{
|
||||
auto& [path, future] = marker_loader;
|
||||
if (future.valid()) {
|
||||
if (future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
|
||||
const auto loaded_marker = future.get();
|
||||
if (loaded_marker) {
|
||||
chosen_marker = loaded_marker;
|
||||
config.marker.folder = path;
|
||||
} else {
|
||||
previews.erase(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::erase_if(discarded_markers, [](const std::future<void>& f){
|
||||
return not f.valid() or f.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
|
||||
});
|
||||
}
|
||||
|
||||
void Markers::display_debug() {
|
||||
if (ImGui::Begin("Markers Debug")) {
|
||||
ImGui::TextUnformatted("chosen_marker :");
|
||||
ImGui::SameLine();
|
||||
if (not chosen_marker) {
|
||||
ImGui::TextDisabled("(empty)");
|
||||
} else {
|
||||
ImGui::TextUnformatted(path_to_utf8_encoded_string((*chosen_marker)->get_folder()).c_str());
|
||||
}
|
||||
ImGui::TextUnformatted("marker_loader :");
|
||||
ImGui::SameLine();
|
||||
if (not std::get<1>(marker_loader).valid()) {
|
||||
ImGui::TextDisabled("(empty)");
|
||||
} else {
|
||||
ImGui::TextUnformatted(path_to_utf8_encoded_string(std::get<0>(marker_loader)).c_str());
|
||||
}
|
||||
if (ImGui::TreeNodeEx("discarded_markers", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
for (const auto& future : discarded_markers) {
|
||||
ImGui::BulletText("%s", fmt::format("{}", fmt::ptr(&future)).c_str());
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
if (ImGui::TreeNodeEx("previews", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
for (const auto& [path, _] : previews) {
|
||||
ImGui::BulletText("%s", fmt::format("{}", path_to_utf8_encoded_string(path)).c_str());
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
if (ImGui::TreeNodeEx("preview_loaders", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
for (const auto& [path, _] : preview_loaders) {
|
||||
ImGui::BulletText("%s", fmt::format("{}", path_to_utf8_encoded_string(path)).c_str());
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
Markers::marker_type Markers::load_default_marker(
|
||||
const std::optional<std::filesystem::path>& chosen_from_config,
|
||||
const std::filesystem::path& markers
|
||||
) {
|
||||
if (chosen_from_config and chosen_from_config->is_relative()) {
|
||||
try {
|
||||
return load_marker_from(markers / *chosen_from_config);
|
||||
} catch (const std::exception& e) {
|
||||
fmt::print("Failed to load marker from preferences");
|
||||
}
|
||||
}
|
||||
return first_available_marker_in(markers);
|
||||
}
|
||||
|
||||
Markers::marker_type Markers::load_marker_async(const std::filesystem::path& chosen, const std::filesystem::path& markers) {
|
||||
try {
|
||||
return load_marker_from(markers / chosen);
|
||||
} catch (const std::exception& e) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void Markers::discard_marker(std::future<marker_type>&& future) {
|
||||
auto other_future = std::move(future);
|
||||
if (other_future.valid()) {
|
||||
other_future.get();
|
||||
}
|
||||
}
|
||||
|
||||
Markers::preview_type Markers::load_marker_preview(const std::filesystem::path& chosen) {
|
||||
for (const auto& file : {"preview.png", "ma15.png"}) {
|
||||
const auto preview_path = chosen / file;
|
||||
if (std::filesystem::exists(preview_path)) {
|
||||
feis::Texture preview;
|
||||
if (preview.load_from_path(preview_path)) {
|
||||
return preview;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
58
src/markers.hpp
Normal file
58
src/markers.hpp
Normal file
@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <future>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
|
||||
#include "config.hpp"
|
||||
#include "marker.hpp"
|
||||
#include "utf8_sfml_redefinitions.hpp"
|
||||
|
||||
/* Deals with loading markers and marker previews asynchronously */
|
||||
class Markers {
|
||||
public:
|
||||
|
||||
using marker_type = std::optional<std::shared_ptr<Marker>>;
|
||||
using marker_loader_type = std::future<marker_type>;
|
||||
using preview_type = std::optional<feis::Texture>;
|
||||
using previews_type = std::map<std::filesystem::path, preview_type>;
|
||||
|
||||
Markers(
|
||||
const std::filesystem::path& markers_folder,
|
||||
config::Config& config
|
||||
);
|
||||
|
||||
void load_marker(const std::filesystem::path& chosen);
|
||||
|
||||
const marker_type& get_chosen_marker() const;
|
||||
|
||||
previews_type::const_iterator cbegin() const;
|
||||
previews_type::const_iterator cend() const;
|
||||
|
||||
void update();
|
||||
|
||||
void display_debug();
|
||||
private:
|
||||
std::filesystem::path markers_folder;
|
||||
config::Config& config;
|
||||
|
||||
// Markers
|
||||
marker_type chosen_marker;
|
||||
std::tuple<std::filesystem::path, std::future<marker_type>> marker_loader;
|
||||
static marker_type load_default_marker(
|
||||
const std::optional<std::filesystem::path>& marker_path_from_config,
|
||||
const std::filesystem::path& markers_folder
|
||||
);
|
||||
static marker_type load_marker_async(
|
||||
const std::filesystem::path& chosen,
|
||||
const std::filesystem::path& markers
|
||||
);
|
||||
std::vector<std::future<void>> discarded_markers;
|
||||
static void discard_marker(std::future<marker_type>&& future);
|
||||
|
||||
previews_type previews;
|
||||
std::map<std::filesystem::path, std::future<preview_type>> preview_loaders;
|
||||
|
||||
static preview_type load_marker_preview(const std::filesystem::path& folder);
|
||||
};
|
@ -27,6 +27,7 @@ sources += files(
|
||||
'long_note_dummy.cpp',
|
||||
'main.cpp',
|
||||
'marker.cpp',
|
||||
'markers.cpp',
|
||||
'mp3_reader.cpp',
|
||||
'note.cpp',
|
||||
'notification.cpp',
|
||||
|
Loading…
Reference in New Issue
Block a user