2022-03-02 23:59:19 +01:00
|
|
|
#pragma once
|
2019-01-03 23:20:35 +01:00
|
|
|
|
2023-03-18 01:54:40 +01:00
|
|
|
#include <future>
|
2022-06-01 00:26:36 +02:00
|
|
|
#include <memory>
|
|
|
|
#include <optional>
|
|
|
|
|
2019-01-03 23:20:35 +01:00
|
|
|
#include <SFML/Audio.hpp>
|
2022-05-11 00:09:02 +02:00
|
|
|
#include <SFML/Audio/SoundSource.hpp>
|
2019-01-12 17:16:20 +01:00
|
|
|
#include <SFML/Graphics.hpp>
|
2022-10-14 02:42:13 +02:00
|
|
|
#include <type_traits>
|
2021-12-31 14:59:39 +01:00
|
|
|
|
2022-04-06 15:47:04 +02:00
|
|
|
|
2023-03-11 12:13:37 +01:00
|
|
|
#include "cache.hpp"
|
2022-11-19 01:43:37 +01:00
|
|
|
#include "config.hpp"
|
2022-06-11 02:26:46 +02:00
|
|
|
#include "custom_sfml_audio/beat_ticks.hpp"
|
2022-10-07 01:47:45 +02:00
|
|
|
#include "custom_sfml_audio/chord_claps.hpp"
|
2022-06-09 00:28:21 +02:00
|
|
|
#include "custom_sfml_audio/note_claps.hpp"
|
2022-06-01 00:26:36 +02:00
|
|
|
#include "custom_sfml_audio/open_music.hpp"
|
|
|
|
#include "custom_sfml_audio/synced_sound_streams.hpp"
|
2023-07-07 01:56:06 +02:00
|
|
|
#include "guess_tempo.hpp"
|
2023-01-09 16:17:46 +01:00
|
|
|
#include "utf8_sfml.hpp"
|
2023-03-11 12:13:37 +01:00
|
|
|
#include "utf8_sfml_redefinitions.hpp"
|
|
|
|
#include "waveform.hpp"
|
2022-06-01 00:26:36 +02:00
|
|
|
#include "widgets/linear_view.hpp"
|
2022-04-04 22:03:10 +02:00
|
|
|
#include "better_note.hpp"
|
2022-03-16 02:10:18 +01:00
|
|
|
#include "better_song.hpp"
|
|
|
|
#include "chart_state.hpp"
|
2022-03-26 00:23:13 +01:00
|
|
|
#include "generic_interval.hpp"
|
2021-12-31 00:57:06 +01:00
|
|
|
#include "history.hpp"
|
2021-12-31 14:59:39 +01:00
|
|
|
#include "marker.hpp"
|
2022-11-09 01:01:58 +01:00
|
|
|
#include "clipboard.hpp"
|
2022-03-23 02:20:07 +01:00
|
|
|
#include "notifications_queue.hpp"
|
2022-04-04 22:03:10 +02:00
|
|
|
#include "playfield.hpp"
|
2019-01-03 23:20:35 +01:00
|
|
|
|
2019-03-28 10:07:19 +01:00
|
|
|
|
2022-06-01 00:26:36 +02:00
|
|
|
const std::string music_stream = "music";
|
2022-10-07 01:47:45 +02:00
|
|
|
const std::string note_clap_stream = "note_clap";
|
|
|
|
const std::string chord_clap_stream = "chord_clap";
|
2022-06-11 02:26:46 +02:00
|
|
|
const std::string beat_tick_stream = "beat_tick";
|
2022-06-01 00:26:36 +02:00
|
|
|
|
2019-03-27 20:37:30 +01:00
|
|
|
/*
|
2021-12-31 14:59:39 +01:00
|
|
|
* The god class, holds everything there is to know about the currently open
|
2022-03-17 02:50:30 +01:00
|
|
|
* file
|
2019-03-27 20:37:30 +01:00
|
|
|
*/
|
2019-03-02 13:47:26 +01:00
|
|
|
class EditorState {
|
2019-01-03 23:20:35 +01:00
|
|
|
public:
|
2023-04-02 22:31:15 +02:00
|
|
|
EditorState(
|
2022-11-19 01:43:37 +01:00
|
|
|
const std::filesystem::path& assets,
|
|
|
|
config::Config& config
|
|
|
|
);
|
2022-03-16 02:10:18 +01:00
|
|
|
EditorState(
|
|
|
|
const better::Song& song,
|
2022-03-17 02:50:30 +01:00
|
|
|
const std::filesystem::path& assets,
|
2022-11-19 01:43:37 +01:00
|
|
|
const std::filesystem::path& save_path,
|
|
|
|
config::Config& config
|
2022-03-17 02:50:30 +01:00
|
|
|
);
|
2022-11-19 01:43:37 +01:00
|
|
|
|
|
|
|
config::Config& config;
|
2022-10-13 00:59:40 +02:00
|
|
|
|
|
|
|
History history;
|
|
|
|
|
2022-04-01 02:30:32 +02:00
|
|
|
better::Song song;
|
2019-01-17 15:37:15 +01:00
|
|
|
|
2022-03-17 02:50:30 +01:00
|
|
|
std::optional<std::filesystem::path> song_path;
|
|
|
|
|
2022-03-16 02:10:18 +01:00
|
|
|
std::optional<ChartState> chart_state;
|
2019-03-26 00:04:29 +01:00
|
|
|
|
2022-05-11 00:09:02 +02:00
|
|
|
SyncedSoundStreams audio;
|
2022-06-09 00:28:21 +02:00
|
|
|
std::shared_ptr<NoteClaps> note_claps;
|
2022-10-07 01:47:45 +02:00
|
|
|
std::shared_ptr<ChordClaps> chord_claps;
|
2022-06-11 02:26:46 +02:00
|
|
|
std::shared_ptr<BeatTicks> beat_ticks;
|
2022-06-01 00:26:36 +02:00
|
|
|
std::optional<std::shared_ptr<OpenMusic>> music = {};
|
2022-11-14 22:27:53 +01:00
|
|
|
bool is_playing_preview_music_from_sss = false;
|
2022-04-07 00:14:01 +02:00
|
|
|
|
2023-03-18 01:54:40 +01:00
|
|
|
std::future<std::optional<waveform::Waveform>> waveform_loader;
|
|
|
|
std::optional<waveform::Waveform> waveform;
|
|
|
|
waveform::Status waveform_status();
|
|
|
|
|
2023-07-07 01:56:06 +02:00
|
|
|
std::future<std::vector<TempoCandidate>> tempo_candidates_loader;
|
|
|
|
std::optional<std::vector<TempoCandidate>> tempo_candidates;
|
2023-05-15 01:44:02 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
2022-04-07 00:14:01 +02:00
|
|
|
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();
|
2019-03-26 00:04:29 +01:00
|
|
|
|
2023-05-15 01:44:02 +02:00
|
|
|
std::optional<feis::Music> preview_audio;
|
2022-03-23 02:20:07 +01:00
|
|
|
|
2022-11-11 01:12:28 +01:00
|
|
|
void play_music_preview();
|
2022-11-14 22:27:53 +01:00
|
|
|
void stop_music_preview();
|
|
|
|
bool music_preview_is_playing() const;
|
|
|
|
sf::Time music_preview_position() const;
|
|
|
|
sf::Time music_preview_duration() const;
|
|
|
|
void update_music_preview_status();
|
2022-11-11 01:12:28 +01:00
|
|
|
|
2019-04-06 22:35:17 +02:00
|
|
|
Playfield playfield;
|
2023-02-27 23:42:37 +01:00
|
|
|
LinearView linear_view;
|
2019-03-26 00:04:29 +01:00
|
|
|
|
2022-04-03 15:59:05 +02:00
|
|
|
std::uint64_t snap = 1;
|
2019-01-16 22:10:20 +01:00
|
|
|
|
2023-03-11 12:13:37 +01:00
|
|
|
std::optional<feis::Texture> jacket;
|
2019-01-12 17:16:20 +01:00
|
|
|
|
2019-03-28 02:16:29 +01:00
|
|
|
bool playing;
|
|
|
|
|
2022-04-16 05:25:03 +02:00
|
|
|
std::variant<sf::Time, Fraction> playback_position;
|
|
|
|
std::variant<sf::Time, Fraction> previous_playback_position;
|
2019-04-09 23:07:22 +02:00
|
|
|
|
2022-03-26 00:23:13 +01:00
|
|
|
const Interval<sf::Time>& get_editable_range();
|
2019-01-17 01:08:38 +01:00
|
|
|
|
2022-10-11 01:54:43 +02:00
|
|
|
bool has_any_audio() const;
|
2022-06-09 00:28:21 +02:00
|
|
|
void toggle_playback();
|
2022-10-07 01:47:45 +02:00
|
|
|
void toggle_note_claps();
|
|
|
|
bool note_claps_are_on() const {return audio.contains_stream(note_clap_stream);};
|
|
|
|
void toggle_clap_on_long_note_ends();
|
|
|
|
bool get_clap_on_long_note_ends() const {return clap_on_long_note_ends;};
|
|
|
|
void toggle_distinct_chord_claps();
|
|
|
|
bool get_distinct_chord_claps() const {return distinct_chord_clap;};
|
2022-06-11 02:26:46 +02:00
|
|
|
void toggle_beat_ticks();
|
2022-10-07 01:47:45 +02:00
|
|
|
bool beat_ticks_are_on() const {return audio.contains_stream(beat_tick_stream);};
|
2022-05-11 00:09:02 +02:00
|
|
|
void play();
|
|
|
|
void pause();
|
|
|
|
void stop();
|
2022-05-31 01:21:12 +02:00
|
|
|
sf::SoundSource::Status get_status();
|
2022-05-11 00:09:02 +02:00
|
|
|
void set_pitch(float pitch);
|
2022-10-05 01:42:50 +02:00
|
|
|
float get_pitch() const;
|
2022-04-16 05:25:03 +02:00
|
|
|
void set_playback_position(std::variant<sf::Time, Fraction> newPosition);
|
2022-06-08 01:05:11 +02:00
|
|
|
sf::Time get_precise_playback_position();
|
2019-02-09 16:05:46 +01:00
|
|
|
|
2022-03-23 02:20:07 +01:00
|
|
|
Fraction current_exact_beats() const;
|
|
|
|
Fraction current_snaped_beats() const;
|
2022-04-16 05:25:03 +02:00
|
|
|
Fraction previous_exact_beats() const;
|
|
|
|
sf::Time current_time() const;
|
|
|
|
sf::Time previous_time() const;
|
2022-03-23 02:20:07 +01:00
|
|
|
Fraction beats_at(sf::Time time) const;
|
|
|
|
sf::Time time_at(Fraction beat) const;
|
|
|
|
Fraction get_snap_step() const;
|
2019-01-12 17:16:20 +01:00
|
|
|
|
2022-10-11 21:23:29 +02:00
|
|
|
bool show_playfield = true;
|
2022-04-09 00:54:06 +02:00
|
|
|
void display_playfield(Marker& marker, Judgement markerEndingState);
|
2022-03-23 02:20:07 +01:00
|
|
|
|
2022-10-11 21:23:29 +02:00
|
|
|
bool show_file_properties = false;
|
|
|
|
void display_file_properties();
|
2022-03-23 02:20:07 +01:00
|
|
|
|
2022-10-11 21:23:29 +02:00
|
|
|
bool show_status = false;
|
2022-03-23 02:20:07 +01:00
|
|
|
void display_status();
|
|
|
|
|
2022-10-11 21:23:29 +02:00
|
|
|
bool show_playback_status = true;
|
2022-03-23 02:20:07 +01:00
|
|
|
void display_playback_status();
|
|
|
|
|
2022-10-11 21:23:29 +02:00
|
|
|
bool show_timeline = true;
|
2022-03-23 02:20:07 +01:00
|
|
|
void display_timeline();
|
|
|
|
|
2022-10-11 21:23:29 +02:00
|
|
|
bool show_chart_list = false;
|
2022-03-24 01:42:41 +01:00
|
|
|
void display_chart_list();
|
2022-03-23 02:20:07 +01:00
|
|
|
|
2022-10-11 21:23:29 +02:00
|
|
|
bool show_linear_view = false;
|
2022-03-23 02:20:07 +01:00
|
|
|
void display_linear_view();
|
|
|
|
|
2022-10-11 21:23:29 +02:00
|
|
|
bool show_sound_settings = false;
|
2022-10-11 01:54:43 +02:00
|
|
|
void display_sound_settings();
|
|
|
|
|
2022-10-11 21:23:29 +02:00
|
|
|
bool show_editor_settings = false;
|
|
|
|
void display_editor_settings();
|
|
|
|
|
2022-10-13 00:59:40 +02:00
|
|
|
bool show_history = false;
|
|
|
|
void display_history();
|
|
|
|
|
2022-10-11 21:23:29 +02:00
|
|
|
bool show_new_chart_dialog = false;
|
|
|
|
bool show_chart_properties = false;
|
2022-10-13 00:59:40 +02:00
|
|
|
|
2022-11-10 00:48:13 +01:00
|
|
|
bool show_timing_menu = false;
|
|
|
|
void display_timing_menu();
|
2019-01-14 21:43:56 +01:00
|
|
|
|
2022-04-02 04:10:09 +02:00
|
|
|
enum class SaveOutcome {
|
|
|
|
UserSaved,
|
|
|
|
UserDeclindedSaving,
|
|
|
|
UserCanceled,
|
|
|
|
NoSavingNeeded,
|
|
|
|
};
|
|
|
|
|
2022-04-09 00:54:06 +02:00
|
|
|
SaveOutcome save_if_needed_and_user_wants_to();
|
2022-04-06 15:47:04 +02:00
|
|
|
|
2022-04-07 00:14:01 +02:00
|
|
|
SaveOutcome save_if_needed();
|
2022-04-02 04:10:09 +02:00
|
|
|
|
2022-11-11 00:06:58 +01:00
|
|
|
SaveOutcome save_asking_for_path();
|
|
|
|
|
2022-04-02 04:10:09 +02:00
|
|
|
bool needs_to_save() const;
|
|
|
|
|
|
|
|
enum class UserWantsToSave {
|
|
|
|
Yes,
|
|
|
|
No,
|
|
|
|
Cancel,
|
|
|
|
};
|
|
|
|
|
|
|
|
UserWantsToSave ask_if_user_wants_to_save() const;
|
2022-04-01 02:30:32 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
If the given song already has a dedicated file on disk, returns its path.
|
|
|
|
Otherwise use a dialog box to ask the user for a path and return it, or
|
|
|
|
return nothing if the user canceled
|
|
|
|
*/
|
|
|
|
std::optional<std::filesystem::path> ask_for_save_path_if_needed();
|
2019-03-28 02:16:29 +01:00
|
|
|
|
2022-10-26 02:08:19 +02:00
|
|
|
void insert_long_note_just_created();
|
2022-03-16 02:10:18 +01:00
|
|
|
|
2022-03-23 02:20:07 +01:00
|
|
|
void move_backwards_in_time();
|
|
|
|
void move_forwards_in_time();
|
|
|
|
|
|
|
|
void undo(NotificationsQueue& nq);
|
|
|
|
void redo(NotificationsQueue& nq);
|
|
|
|
|
2022-11-09 01:01:58 +01:00
|
|
|
void cut(NotificationsQueue& nq);
|
|
|
|
void copy(NotificationsQueue& nq);
|
|
|
|
void paste(NotificationsQueue& nq);
|
|
|
|
void delete_(NotificationsQueue& nq);
|
|
|
|
|
|
|
|
void discard_selection();
|
|
|
|
|
2022-04-01 01:41:47 +02:00
|
|
|
void save(const std::filesystem::path& path);
|
2022-03-31 03:50:15 +02:00
|
|
|
|
2022-11-10 23:39:39 +01:00
|
|
|
void insert_chart(const std::string& name, const better::Chart& chart);
|
|
|
|
void insert_chart_and_push_history(const std::string& name, const better::Chart& chart);
|
|
|
|
void erase_chart(const std::string& name);
|
|
|
|
void erase_chart_and_push_history(const std::string& name);
|
2022-04-04 04:30:02 +02:00
|
|
|
void open_chart(const std::string& name);
|
2022-11-10 23:39:39 +01:00
|
|
|
void close_chart();
|
2022-04-04 04:30:02 +02:00
|
|
|
|
2022-04-07 00:14:01 +02:00
|
|
|
void update_visible_notes();
|
|
|
|
|
2022-10-14 02:42:13 +02:00
|
|
|
void reload_jacket();
|
|
|
|
void reload_music();
|
|
|
|
void reload_preview_audio();
|
2022-10-26 23:35:00 +02:00
|
|
|
void reload_applicable_timing();
|
2022-10-26 02:08:19 +02:00
|
|
|
void reload_sounds_that_depend_on_notes();
|
|
|
|
void reload_sounds_that_depend_on_timing();
|
|
|
|
void reload_all_sounds();
|
2023-05-03 23:17:26 +02:00
|
|
|
void reload_editable_range();
|
2022-10-26 02:08:19 +02:00
|
|
|
|
2023-05-15 01:44:02 +02:00
|
|
|
void frame_hook();
|
|
|
|
|
2022-10-26 23:35:00 +02:00
|
|
|
private:
|
|
|
|
|
2022-04-07 00:14:01 +02:00
|
|
|
int volume = 10; // 0 -> 10
|
|
|
|
int speed = 10; // 1 -> 20
|
|
|
|
|
2022-10-07 01:47:45 +02:00
|
|
|
bool clap_on_long_note_ends = false;
|
|
|
|
bool distinct_chord_clap = false;
|
|
|
|
|
2022-10-11 01:54:43 +02:00
|
|
|
// Playback status used when there is no actual audio being played
|
|
|
|
sf::SoundSource::Status status = sf::SoundSource::Stopped;
|
|
|
|
|
2022-03-17 02:50:30 +01:00
|
|
|
/*
|
|
|
|
sf::Time bounds (in the audio file "coordinates") which are accessible
|
|
|
|
(and maybe editable) from the editor, can extend before and after
|
2022-03-23 02:20:07 +01:00
|
|
|
the audio file
|
2022-03-17 02:50:30 +01:00
|
|
|
*/
|
2022-03-26 00:23:13 +01:00
|
|
|
Interval<sf::Time> editable_range;
|
2022-04-19 02:07:56 +02:00
|
|
|
Interval<sf::Time> choose_editable_range();
|
2022-06-08 01:05:11 +02:00
|
|
|
void clear_music();
|
2022-03-16 02:10:18 +01:00
|
|
|
|
2022-10-26 02:08:19 +02:00
|
|
|
std::shared_ptr<better::Timing> applicable_timing;
|
2022-10-26 23:35:00 +02:00
|
|
|
TimingOrigin timing_origin();
|
2022-03-17 02:50:30 +01:00
|
|
|
|
|
|
|
std::filesystem::path assets;
|
2023-03-17 01:49:34 +01:00
|
|
|
|
|
|
|
std::optional<std::filesystem::path> full_audio_path();
|
2019-01-03 23:20:35 +01:00
|
|
|
};
|
|
|
|
|
2022-04-01 02:30:32 +02:00
|
|
|
namespace feis {
|
2022-11-11 00:06:58 +01:00
|
|
|
void force_save(
|
2022-04-09 00:54:06 +02:00
|
|
|
std::optional<EditorState>& ed,
|
|
|
|
NotificationsQueue& nq
|
|
|
|
);
|
|
|
|
|
|
|
|
void save_ask_open(
|
|
|
|
std::optional<EditorState>& ed,
|
|
|
|
const std::filesystem::path& assets,
|
2022-11-19 01:43:37 +01:00
|
|
|
const std::filesystem::path& settings,
|
|
|
|
config::Config& config
|
2022-04-09 00:54:06 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
void save_open(
|
|
|
|
std::optional<EditorState>& ed,
|
|
|
|
const std::filesystem::path& file,
|
|
|
|
const std::filesystem::path& assets,
|
2022-11-19 01:43:37 +01:00
|
|
|
const std::filesystem::path& settings,
|
|
|
|
config::Config& config
|
2022-04-09 00:54:06 +02:00
|
|
|
);
|
|
|
|
|
2022-04-03 15:59:05 +02:00
|
|
|
void open_from_file(
|
2022-01-04 01:31:17 +01:00
|
|
|
std::optional<EditorState>& ed,
|
2022-04-09 00:54:06 +02:00
|
|
|
const std::filesystem::path& file,
|
|
|
|
const std::filesystem::path& assets,
|
2022-11-19 01:43:37 +01:00
|
|
|
const std::filesystem::path& settings,
|
|
|
|
config::Config& config
|
2022-01-04 01:31:17 +01:00
|
|
|
);
|
2019-01-16 01:59:02 +01:00
|
|
|
|
2022-04-09 00:54:06 +02:00
|
|
|
void save_close(std::optional<EditorState>& ed);
|
|
|
|
|
2019-01-16 01:59:02 +01:00
|
|
|
class NewChartDialog {
|
|
|
|
public:
|
2022-04-04 04:30:02 +02:00
|
|
|
std::optional<std::pair<std::string, better::Chart>> display(EditorState& editorState);
|
2021-12-31 14:59:39 +01:00
|
|
|
void resetValues() {
|
|
|
|
level = 1;
|
|
|
|
difficulty = "";
|
2022-04-04 04:30:02 +02:00
|
|
|
combo_preview = "";
|
|
|
|
show_custom_dif_name = false;
|
2021-12-31 14:59:39 +01:00
|
|
|
};
|
2019-01-16 01:59:02 +01:00
|
|
|
|
|
|
|
private:
|
2022-04-04 04:30:02 +02:00
|
|
|
Decimal level = 1;
|
2019-01-16 01:59:02 +01:00
|
|
|
std::string difficulty;
|
2022-04-04 04:30:02 +02:00
|
|
|
std::string combo_preview;
|
|
|
|
bool show_custom_dif_name = false;
|
2019-01-16 01:59:02 +01:00
|
|
|
};
|
2019-01-16 19:12:01 +01:00
|
|
|
|
|
|
|
class ChartPropertiesDialog {
|
|
|
|
public:
|
2022-04-04 04:30:02 +02:00
|
|
|
void display(EditorState& editorState);
|
|
|
|
bool should_refresh_values = true;
|
2019-01-16 19:12:01 +01:00
|
|
|
|
|
|
|
private:
|
2022-04-04 04:30:02 +02:00
|
|
|
Decimal level;
|
2019-03-02 13:47:26 +01:00
|
|
|
std::string difficulty_name;
|
2022-04-04 04:30:02 +02:00
|
|
|
std::string combo_preview;
|
|
|
|
std::set<std::string> difficulty_names_in_use;
|
|
|
|
bool show_custom_dif_name = false;
|
2019-01-16 19:12:01 +01:00
|
|
|
};
|
2022-11-15 01:14:03 +01:00
|
|
|
|
|
|
|
void display_shortcuts_help(bool& show);
|
2022-11-15 22:04:29 +01:00
|
|
|
void display_about_menu(bool& show);
|
2019-01-13 22:29:29 +01:00
|
|
|
}
|