mirror of
https://gitlab.com/square-game-liberation-front/F.E.I.S.git
synced 2024-11-14 11:07:44 +01:00
First working bits of waveform mode for the linear view
This commit is contained in:
parent
bb7c1f8593
commit
d4aa0f3532
@ -100,6 +100,15 @@ namespace better {
|
||||
const auto last_element = events_by_beats.upper_bound(bpm_event_type{last, 0, 1});
|
||||
std::for_each(first_element, last_element, [&](const bpm_event_type& ptr){cb(ptr);});
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
void for_each_event_between(const sf::Time& start, const sf::Time& end, const Callback& cb) const {
|
||||
for_each_event_between(
|
||||
beats_at(start),
|
||||
beats_at(end),
|
||||
cb
|
||||
);
|
||||
}
|
||||
|
||||
bool operator==(const Timing&) const = default;
|
||||
|
||||
|
@ -14,20 +14,24 @@ namespace Toolkit {
|
||||
template <class Key, class Value>
|
||||
class Cache {
|
||||
public:
|
||||
using key_type = Key;
|
||||
using value_type = Value;
|
||||
using reference_type = std::reference_wrapper<Value>;
|
||||
using const_reference_type = std::reference_wrapper<const Value>;
|
||||
Cache(std::function<Value(Key)> _load_resource): load_resource(_load_resource) {};
|
||||
|
||||
// Does not trigger loading
|
||||
std::optional<std::reference_wrapper<Value>> get(const Key& key) const {
|
||||
std::optional<const_reference_type> get(const Key& key) const {
|
||||
std::shared_lock lock{mapping_mutex};
|
||||
if (has(key)) {
|
||||
return mapping.at(key);
|
||||
return std::cref(mapping.at(key));
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// Returns empty if not already loaded
|
||||
std::optional<std::reference_wrapper<Value>> async_load(const Key& key) {
|
||||
std::optional<reference_type> async_load(const Key& key) {
|
||||
if (not has(key)) {
|
||||
if (not is_loading(key)) {
|
||||
async_emplace(key);
|
||||
@ -60,7 +64,7 @@ namespace Toolkit {
|
||||
}
|
||||
}
|
||||
|
||||
std::reference_wrapper<Value> blocking_load(const Key& key) {
|
||||
reference_type blocking_load(const Key& key) {
|
||||
std::shared_lock lock{mapping_mutex};
|
||||
blocking_emplace(key);
|
||||
return mapping.at(key);
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <variant>
|
||||
|
||||
#include "linear_view_colors.hpp"
|
||||
#include "linear_view_mode.hpp"
|
||||
#include "marker.hpp"
|
||||
#include "nowide/fstream.hpp"
|
||||
#include "variant_visitor.hpp"
|
||||
@ -47,6 +48,7 @@ void config::LinearView::load_from_v1_0_0_table(const toml::table& tbl) {
|
||||
return;
|
||||
}
|
||||
const auto linear_view_table = tbl["linear_view"].ref<toml::table>();
|
||||
mode = linear_view::mode::load_from_v1_0_0_table(linear_view_table);
|
||||
colors.load_from_v1_0_0_table(linear_view_table);
|
||||
sizes.load_from_v1_0_0_table(linear_view_table);
|
||||
lane_order = linear_view::lane_order::load_from_v1_0_0_table(linear_view_table);
|
||||
@ -61,6 +63,7 @@ void config::LinearView::load_from_v1_0_0_table(const toml::table& tbl) {
|
||||
|
||||
void config::LinearView::dump_as_v1_0_0(toml::table& tbl) {
|
||||
toml::table linear_view_table;
|
||||
linear_view::mode::dump_as_v1_0_0(mode, linear_view_table);
|
||||
colors.dump_as_v1_0_0(linear_view_table);
|
||||
sizes.dump_as_v1_0_0(linear_view_table);
|
||||
linear_view::lane_order::dump_as_v1_0_0(lane_order, linear_view_table);
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include "quantization_colors.hpp"
|
||||
#include "linear_view_colors.hpp"
|
||||
#include "linear_view_mode.hpp"
|
||||
#include "linear_view_sizes.hpp"
|
||||
#include "marker.hpp"
|
||||
#include "widgets/lane_order.hpp"
|
||||
@ -22,6 +23,7 @@ namespace config {
|
||||
};
|
||||
|
||||
struct LinearView {
|
||||
linear_view::Mode mode;
|
||||
linear_view::Colors colors;
|
||||
linear_view::Sizes sizes;
|
||||
linear_view::LaneOrder lane_order;
|
||||
|
@ -980,16 +980,18 @@ void EditorState::display_linear_view() {
|
||||
if (chart_state) {
|
||||
auto header_height = ImGui::GetFontSize() + ImGui::GetStyle().FramePadding.y * 2.f;
|
||||
ImGui::SetCursorPos({0, header_height});
|
||||
linear_view.draw(
|
||||
LinearView::DrawArgs draw_args {
|
||||
ImGui::GetWindowDrawList(),
|
||||
*chart_state,
|
||||
waveform_cache.get(song.metadata.audio),
|
||||
*applicable_timing,
|
||||
current_exact_beats(),
|
||||
beats_at(editable_range.end),
|
||||
get_snap_step(),
|
||||
ImGui::GetContentRegionMax(),
|
||||
ImGui::GetCursorScreenPos()
|
||||
);
|
||||
};
|
||||
linear_view.draw(draw_args);
|
||||
} else {
|
||||
ImGui::TextDisabled("- no chart selected -");
|
||||
}
|
||||
@ -1422,7 +1424,7 @@ void EditorState::reload_music() {
|
||||
const auto absolute_music_path = song_path->parent_path() / song.metadata.audio;
|
||||
try {
|
||||
music.emplace(std::make_shared<OpenMusic>(absolute_music_path));
|
||||
waveform_cache.async_emplace(absolute_music_path);
|
||||
waveform_cache.async_emplace(song.metadata.audio);
|
||||
} catch (const std::exception& e) {
|
||||
clear_music();
|
||||
}
|
||||
|
32
src/linear_view_mode.cpp
Normal file
32
src/linear_view_mode.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
#include "linear_view_mode.hpp"
|
||||
|
||||
#include "variant_visitor.hpp"
|
||||
|
||||
namespace linear_view {
|
||||
Mode load_from_v1_0_0_table(const toml::table& linear_view) {
|
||||
const auto mode_string = linear_view["mode"].value<std::string>();
|
||||
if (not mode_string) {
|
||||
return linear_view::default_mode;
|
||||
}
|
||||
|
||||
if (*mode_string == "beats") {
|
||||
return mode::Beats{};
|
||||
} else if (*mode_string == "waveform") {
|
||||
return mode::Waveform{};
|
||||
}
|
||||
|
||||
return linear_view::default_mode;
|
||||
}
|
||||
|
||||
void dump_as_v1_0_0(const Mode& mode, toml::table& linear_view) {
|
||||
const auto _dump = VariantVisitor {
|
||||
[&](const mode::Beats&) {
|
||||
linear_view.insert_or_assign("mode", "beats");
|
||||
},
|
||||
[&](const mode::Waveform&) {
|
||||
linear_view.insert_or_assign("mode", "waveform");
|
||||
},
|
||||
};
|
||||
std::visit(_dump, mode);
|
||||
}
|
||||
}
|
21
src/linear_view_mode.hpp
Normal file
21
src/linear_view_mode.hpp
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <variant>
|
||||
|
||||
#include <toml++/toml.h>
|
||||
|
||||
namespace linear_view {
|
||||
namespace mode {
|
||||
struct Beats {};
|
||||
struct Waveform {};
|
||||
}
|
||||
|
||||
using Mode = std::variant<mode::Beats, mode::Waveform>;
|
||||
|
||||
namespace mode {
|
||||
linear_view::Mode load_from_v1_0_0_table(const toml::table& linear_view);
|
||||
void dump_as_v1_0_0(const linear_view::Mode& mode, toml::table& linear_view);
|
||||
}
|
||||
|
||||
const Mode default_mode = mode::Beats{};
|
||||
}
|
@ -33,6 +33,7 @@ sources += files(
|
||||
'toolbox.cpp',
|
||||
'utf8_file_input_stream.cpp',
|
||||
'utf8_strings.cpp',
|
||||
'waveform.cpp'
|
||||
)
|
||||
|
||||
conf_data = configuration_data()
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include "variant_visitor.hpp"
|
||||
|
||||
namespace linear_view {
|
||||
sf::Color QuantizationColors::color_at_beat(const Fraction& time) {
|
||||
sf::Color QuantizationColors::color_at_beat(const Fraction& time) const {
|
||||
const auto denominator = time.denominator();
|
||||
if (denominator > palette.rbegin()->first) {
|
||||
return default_;
|
||||
|
@ -20,7 +20,7 @@ namespace linear_view {
|
||||
{16, {68, 254, 0}}
|
||||
}};
|
||||
sf::Color default_ = {156, 156, 156};
|
||||
sf::Color color_at_beat(const Fraction& time);
|
||||
sf::Color color_at_beat(const Fraction& time) const;
|
||||
|
||||
void load_from_v1_0_0_table(const toml::table& linear_view_table);
|
||||
void dump_as_v1_0_0(toml::table& linear_view_table);
|
||||
|
@ -59,6 +59,10 @@ Fraction::operator double() const {
|
||||
return value.get_d();
|
||||
};
|
||||
|
||||
Fraction::operator float() const {
|
||||
return static_cast<float>(value.get_d());
|
||||
};
|
||||
|
||||
const mpz_class& Fraction::numerator() const {
|
||||
return value.get_num();
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ public:
|
||||
explicit operator std::int64_t() const;
|
||||
explicit operator std::uint64_t() const;
|
||||
explicit operator double() const;
|
||||
explicit operator float() const;
|
||||
|
||||
const mpz_class& numerator() const;
|
||||
const mpz_class& denominator() const;
|
||||
|
@ -111,11 +111,6 @@ public:
|
||||
private:
|
||||
T a;
|
||||
T b;
|
||||
|
||||
public:
|
||||
void setB(T b) { AffineTransform::b = b; }
|
||||
|
||||
private:
|
||||
T low_input;
|
||||
T high_input;
|
||||
T low_output;
|
||||
|
@ -64,17 +64,22 @@ namespace waveform {
|
||||
|
||||
std::optional<Waveform> compute_waveform(const std::filesystem::path& audio) {
|
||||
feis::HoldFileStreamMixin<sf::InputSoundFile> sound_file;
|
||||
try {
|
||||
sound_file.open_from_path(audio);
|
||||
} catch (const std::exception&) {
|
||||
if (not sound_file.open_from_path(audio)) {
|
||||
return {};
|
||||
}
|
||||
Waveform waveform;
|
||||
Waveform waveform{
|
||||
{},
|
||||
{},
|
||||
sound_file.getSampleRate(),
|
||||
sound_file.getChannelCount()
|
||||
};
|
||||
unsigned int size = 8;
|
||||
waveform[size] = load_initial_summary(sound_file, size);
|
||||
while (waveform.size() < 10) {
|
||||
waveform[size * 2] = downsample_to_half(waveform.rbegin()->second);
|
||||
waveform.channels_per_chunk_size[size] = load_initial_summary(sound_file, size);
|
||||
waveform.chunk_sizes.push_back(size);
|
||||
while (waveform.channels_per_chunk_size.size() < 10) {
|
||||
size *= 2;
|
||||
waveform.channels_per_chunk_size[size] = downsample_to_half(waveform.channels_per_chunk_size.rbegin()->second);
|
||||
waveform.chunk_sizes.push_back(size);
|
||||
}
|
||||
return waveform;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
@ -20,7 +21,12 @@ namespace waveform {
|
||||
|
||||
using DataFrame = std::vector<DataPoint>;
|
||||
using Channels = std::vector<DataFrame>;
|
||||
using Waveform = std::map<unsigned int, Channels>;
|
||||
struct Waveform {
|
||||
std::map<unsigned int, Channels> channels_per_chunk_size;
|
||||
std::vector<unsigned int> chunk_sizes; // for fast nth chunk size access
|
||||
std::size_t sample_rate;
|
||||
std::size_t channel_count;
|
||||
};
|
||||
|
||||
Channels load_initial_summary(
|
||||
feis::HoldFileStreamMixin<sf::InputSoundFile>& sound_file,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -43,69 +43,6 @@ const sf::Color reference_note_grey = {134, 110, 116};
|
||||
|
||||
|
||||
|
||||
namespace linear_view {
|
||||
namespace mode {
|
||||
struct Beats {};
|
||||
struct Waveform {};
|
||||
}
|
||||
using Mode = std::variant<mode::Beats, mode::Waveform>;
|
||||
}
|
||||
|
||||
class LinearView {
|
||||
public:
|
||||
LinearView(std::filesystem::path assets, config::Config& config);
|
||||
|
||||
struct draw_args_type {
|
||||
ImDrawList* draw_list;
|
||||
ChartState& chart_state;
|
||||
const waveform::Cache& waveform_cache;
|
||||
const better::Timing& timing;
|
||||
const Fraction& current_beat;
|
||||
const Fraction& last_editable_beat;
|
||||
const Fraction& snap;
|
||||
const sf::Vector2f& size;
|
||||
const sf::Vector2f& origin;
|
||||
};
|
||||
|
||||
void draw(draw_args_type& args);
|
||||
|
||||
void set_zoom(int zoom);
|
||||
void zoom_in() { set_zoom(zoom + 1); };
|
||||
void zoom_out() { set_zoom(zoom - 1); };
|
||||
float time_factor() { return std::pow(1.25, static_cast<double>(zoom)); };
|
||||
|
||||
bool shouldDisplaySettings;
|
||||
|
||||
void display_settings();
|
||||
|
||||
private:
|
||||
void draw_in_beats_mode(draw_args_type& args);
|
||||
void draw_in_waveform_mode(draw_args_type& args);
|
||||
linear_view::Mode mode;
|
||||
std::string mode_name();
|
||||
linear_view::Colors& colors;
|
||||
linear_view::Sizes& sizes;
|
||||
const sf::Time& collision_zone;
|
||||
|
||||
AffineTransform<Fraction> beats_to_pixels_proportional;
|
||||
AffineTransform<Fraction> seconds_to_pixels_proportional;
|
||||
|
||||
void reload_transforms();
|
||||
|
||||
int& zoom;
|
||||
|
||||
bool& use_quantization_colors;
|
||||
linear_view::QuantizationColors& quantization_colors;
|
||||
|
||||
SelectionRectangle selection_rectangle;
|
||||
bool started_selection_inside_window = false;
|
||||
bool any_bpm_button_hovered = false;
|
||||
|
||||
linear_view::LaneOrder& lane_order;
|
||||
std::string lane_order_name() const;
|
||||
std::optional<unsigned int> button_to_lane(const better::Position& button);
|
||||
};
|
||||
|
||||
namespace linear_view {
|
||||
struct ComputedSizes {
|
||||
int x;
|
||||
@ -132,6 +69,136 @@ namespace linear_view {
|
||||
);
|
||||
}
|
||||
|
||||
class LinearView {
|
||||
public:
|
||||
LinearView(std::filesystem::path assets, config::Config& config);
|
||||
|
||||
struct DrawArgs {
|
||||
ImDrawList* draw_list;
|
||||
ChartState& chart_state;
|
||||
const std::optional<waveform::Cache::const_reference_type> waveform;
|
||||
const better::Timing& timing;
|
||||
const Fraction& current_beat;
|
||||
const Fraction& last_editable_beat;
|
||||
const Fraction& snap;
|
||||
const sf::Vector2f& size;
|
||||
const sf::Vector2f& origin;
|
||||
};
|
||||
|
||||
void draw(DrawArgs& args);
|
||||
|
||||
void set_zoom(int zoom);
|
||||
void zoom_in() { set_zoom(zoom + 1); };
|
||||
void zoom_out() { set_zoom(zoom - 1); };
|
||||
float time_factor() { return std::pow(1.25, static_cast<double>(zoom)); };
|
||||
|
||||
bool shouldDisplaySettings;
|
||||
|
||||
void display_settings();
|
||||
|
||||
private:
|
||||
void draw_in_beats_mode(DrawArgs& args);
|
||||
void draw_in_waveform_mode(DrawArgs& args);
|
||||
void draw_tap_note(
|
||||
LinearView::DrawArgs& args,
|
||||
const linear_view::ComputedSizes& computed_sizes,
|
||||
const better::TapNote& tap_note,
|
||||
const sf::Vector2f note_pos,
|
||||
const sf::Time collision_zone,
|
||||
const float collision_zone_y,
|
||||
const float collision_zone_height
|
||||
);
|
||||
void draw_long_note(
|
||||
LinearView::DrawArgs& args,
|
||||
const linear_view::ComputedSizes& computed_sizes,
|
||||
const better::LongNote& long_note,
|
||||
const sf::Vector2f note_pos,
|
||||
const float long_note_rect_height,
|
||||
const sf::Time collision_zone,
|
||||
const float collision_zone_y,
|
||||
const float collision_zone_height
|
||||
);
|
||||
|
||||
template<class T>
|
||||
void draw_notes(
|
||||
const sf::Time first_visible_second,
|
||||
const sf::Time last_visible_second,
|
||||
const ChartState& chart_state,
|
||||
const better::Timing& timing,
|
||||
const T& draw_one_note
|
||||
) {
|
||||
const auto first_visible_collision_zone = timing.beats_at(first_visible_second - collision_zone * 0.5f);
|
||||
const auto last_visible_collision_zone = timing.beats_at(last_visible_second + collision_zone * 0.5f);
|
||||
chart_state.chart.notes->in(
|
||||
first_visible_collision_zone,
|
||||
last_visible_collision_zone,
|
||||
[&](const better::Notes::iterator& it){
|
||||
it->second.visit(draw_one_note);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
template<class T>
|
||||
void draw_long_note_dummy(
|
||||
const ChartState& chart_state,
|
||||
const Fraction& snap,
|
||||
const T& draw_one_note
|
||||
) {
|
||||
if (chart_state.long_note_being_created.has_value()) {
|
||||
draw_one_note(
|
||||
make_long_note_dummy_for_linear_view(
|
||||
*chart_state.long_note_being_created,
|
||||
snap
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
void draw_cursor(
|
||||
ImDrawList* draw_list,
|
||||
const sf::Vector2f& origin,
|
||||
const linear_view::ComputedSizes& computed_sizes
|
||||
);
|
||||
void draw_time_selection(
|
||||
ImDrawList* draw_list,
|
||||
const sf::Vector2f& origin,
|
||||
const ChartState& chart_state,
|
||||
const linear_view::ComputedSizes& computed_sizes,
|
||||
const std::function<float(const Fraction&)> beats_to_absolute_pixels
|
||||
);
|
||||
void handle_mouse_selection(
|
||||
ImDrawList* draw_list,
|
||||
const sf::Vector2f& origin,
|
||||
ChartState& chart_state,
|
||||
const better::Timing& timing,
|
||||
const linear_view::ComputedSizes& computed_sizes,
|
||||
const std::function<Fraction(float)> absolute_pixels_to_beats
|
||||
);
|
||||
|
||||
linear_view::Mode& mode;
|
||||
std::string mode_name();
|
||||
linear_view::Colors& colors;
|
||||
linear_view::Sizes& sizes;
|
||||
const sf::Time& collision_zone;
|
||||
|
||||
AffineTransform<Fraction> beats_to_pixels_proportional;
|
||||
|
||||
void reload_transforms();
|
||||
|
||||
int& zoom;
|
||||
|
||||
bool& use_quantization_colors;
|
||||
linear_view::QuantizationColors& quantization_colors;
|
||||
|
||||
SelectionRectangle selection_rectangle;
|
||||
bool started_selection_inside_window = false;
|
||||
bool any_bpm_button_hovered = false;
|
||||
|
||||
linear_view::LaneOrder& lane_order;
|
||||
std::string lane_order_name() const;
|
||||
std::optional<unsigned int> button_to_lane(const better::Position& button);
|
||||
};
|
||||
|
||||
void draw_rectangle(
|
||||
ImDrawList* draw_list,
|
||||
const sf::Vector2f& pos,
|
||||
|
@ -3,5 +3,4 @@ sources += files([
|
||||
'density_graph.cpp',
|
||||
'lane_order.cpp',
|
||||
'linear_view.cpp',
|
||||
'linear_view_waveform_mode.cpp',
|
||||
])
|
Loading…
Reference in New Issue
Block a user