Save sound params

This commit is contained in:
Stepland 2023-07-12 00:59:41 +02:00
parent 10bae5b0c7
commit 2d4a4219e9
5 changed files with 141 additions and 67 deletions

View File

@ -1,11 +1,11 @@
# v2.0.0
*(+ 2.0.0-rc.1)*
## 🥝🍇🍓🍊🍏 New Stuff 🍏🍊🍓🍇🥝
- BPM Changes
- BPM Autodetect
- Waveform mode for the Linear View
- memon 1.0 + 0.3 + 0.2 support
- BPM Changes
- MP3 support
- BPM Autodetect
## 🍒 Small improvements 🍒
- Claps and Beats ticks should now be perfectly synced !
@ -35,6 +35,7 @@
- Support for negative time playback (lead in before the song starts)
- 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
## 🚧 Changes 🚧
- Force using the asset folder next to the executable

View File

@ -94,6 +94,48 @@ void config::Editor::dump_as_v1_0_0(toml::table& tbl) {
});
}
void config::Sound::load_from_v1_0_0_table(const toml::table& tbl) {
if (not tbl["sound"].is_table()) {
return;
}
const auto sound_table = tbl["sound"].ref<toml::table>();
if (sound_table["music_volume"].is_integer()) {
const auto val = sound_table["music_volume"].value<int>();
music_volume = std::clamp(*val, 0, 10);
}
if (sound_table["beat_tick"].is_boolean()) {
beat_tick = *sound_table["beat_tick"].value<bool>();
}
if (sound_table["beat_tick_volume"].is_integer()) {
const auto val = sound_table["beat_tick_volume"].value<int>();
beat_tick_volume = std::clamp(*val, 0, 10);
}
if (sound_table["note_clap"].is_boolean()) {
note_clap = *sound_table["note_clap"].value<bool>();
}
if (sound_table["note_clap_volume"].is_integer()) {
const auto val = sound_table["note_clap_volume"].value<int>();
note_clap_volume = std::clamp(*val, 0, 10);
}
if (sound_table["clap_on_long_note_ends"].is_boolean()) {
clap_on_long_note_ends = *sound_table["clap_on_long_note_ends"].value<bool>();
}
if (sound_table["distinct_chord_clap"].is_boolean()) {
distinct_chord_clap = *sound_table["distinct_chord_clap"].value<bool>();
}
}
void config::Sound::dump_as_v1_0_0(toml::table& tbl) {
tbl.insert_or_assign("sound", toml::table{
{"music_volume", music_volume},
{"beat_tick", beat_tick},
{"beat_tick_volume", beat_tick_volume},
{"note_clap", note_clap},
{"note_clap_volume", note_clap_volume},
{"clap_on_long_note_ends", clap_on_long_note_ends},
{"distinct_chord_clap", distinct_chord_clap}
});
}
config::Config::Config(const std::filesystem::path& settings) :
config_path(settings / "config.toml")
@ -132,6 +174,7 @@ toml::table config::Config::dump_as_v1_0_0() {
marker.dump_as_v1_0_0(tbl);
linear_view.dump_as_v1_0_0(tbl);
editor.dump_as_v1_0_0(tbl);
sound.dump_as_v1_0_0(tbl);
return tbl;
}
@ -151,4 +194,5 @@ void config::Config::load_from_v1_0_0_table(const toml::table& tbl) {
marker.load_from_v1_0_0_table(tbl);
linear_view.load_from_v1_0_0_table(tbl);
editor.load_from_v1_0_0_table(tbl);
sound.load_from_v1_0_0_table(tbl);
}

View File

@ -43,6 +43,19 @@ namespace config {
void dump_as_v1_0_0(toml::table& tbl);
};
struct Sound {
int music_volume = 10;
bool beat_tick = false;
int beat_tick_volume = 10;
bool note_clap = false;
int note_clap_volume = 10;
bool clap_on_long_note_ends = false;
bool distinct_chord_clap = false;
void load_from_v1_0_0_table(const toml::table& tbl);
void dump_as_v1_0_0(toml::table& tbl);
};
/* RAII-style class that holds settings we wish to save on disk and saves
them upon being destroyed */
class Config {
@ -55,6 +68,7 @@ namespace config {
Marker marker;
LinearView linear_view;
Editor editor;
Sound sound;
private:
void load_from_v1_0_0_table(const toml::table& tbl);

View File

@ -111,22 +111,22 @@ waveform::Status EditorState::waveform_status() {
}
int EditorState::get_volume() const {
return volume;
return config.sound.music_volume;
}
void EditorState::set_volume(int volume_) {
if (music.has_value()) {
(**music).set_volume(volume_);
volume = (**music).get_volume();
config.sound.music_volume = (**music).get_volume();
}
}
void EditorState::volume_up() {
set_volume(volume + 1);
set_volume(get_volume() + 1);
}
void EditorState::volume_down() {
set_volume(volume - 1);
set_volume(get_volume() - 1);
}
int EditorState::get_speed() const {
@ -240,19 +240,16 @@ void EditorState::toggle_playback() {
}
void EditorState::toggle_note_claps() {
if (
audio.contains_stream(note_clap_stream)
or audio.contains_stream(chord_clap_stream)
) {
if (note_clap_stream_is_on()) {
audio.update_streams({}, {note_clap_stream, chord_clap_stream});
} else {
note_claps = note_claps->with_params(
get_pitch(),
not distinct_chord_clap,
clap_on_long_note_ends
not config.sound.distinct_chord_clap,
config.sound.clap_on_long_note_ends
);
std::map<std::string, NewStream> streams = {{note_clap_stream, {note_claps, true}}};
if (distinct_chord_clap) {
if (config.sound.distinct_chord_clap) {
chord_claps = chord_claps->with_pitch(get_pitch());
streams[chord_clap_stream] = {chord_claps, true};
}
@ -261,23 +258,21 @@ void EditorState::toggle_note_claps() {
}
void EditorState::toggle_clap_on_long_note_ends() {
clap_on_long_note_ends = not clap_on_long_note_ends;
note_claps = note_claps->with_params(
get_pitch(),
not distinct_chord_clap,
clap_on_long_note_ends
not config.sound.distinct_chord_clap,
config.sound.clap_on_long_note_ends
);
audio.update_streams({{note_clap_stream, {note_claps, true}}});
}
void EditorState::toggle_distinct_chord_claps() {
distinct_chord_clap = not distinct_chord_clap;
note_claps = note_claps->with_params(
get_pitch(),
not distinct_chord_clap,
clap_on_long_note_ends
not config.sound.distinct_chord_clap,
config.sound.clap_on_long_note_ends
);
if (distinct_chord_clap) {
if (config.sound.distinct_chord_clap) {
chord_claps = chord_claps->with_pitch(get_pitch());
audio.update_streams(
{
@ -294,7 +289,7 @@ void EditorState::toggle_distinct_chord_claps() {
}
void EditorState::toggle_beat_ticks() {
if (audio.contains_stream(beat_tick_stream)) {
if (beat_tick_stream_is_on()) {
audio.remove_stream(beat_tick_stream);
} else {
beat_ticks = beat_ticks->with_pitch(get_pitch());
@ -1028,56 +1023,56 @@ void EditorState::display_linear_view() {
void EditorState::display_sound_settings() {
if (ImGui::Begin("Sound Settings", &show_sound_settings)) {
if (ImGui::TreeNodeEx("Music", ImGuiTreeNodeFlags_DefaultOpen)) {
ImGui::BeginDisabled(not music.has_value());
{
auto volume = get_volume();
if (ImGui::SliderInt("Volume##Music", &volume, 0, 10)) {
set_volume(volume);
}
const auto music_exists = music.has_value();
if (not music_exists) {
ImGui::BeginDisabled();
}
if (ImGui::SliderInt("Volume##Music", &config.sound.music_volume, 0, 10)) {
set_volume(config.sound.music_volume);
}
if (not music_exists) {
ImGui::EndDisabled();
}
ImGui::EndDisabled();
ImGui::TreePop();
}
if (ImGui::TreeNodeEx("Beat Tick", ImGuiTreeNodeFlags_DefaultOpen)) {
bool beat_tick = beat_ticks_are_on();
if (ImGui::Checkbox("On/Off##Beat Tick", &beat_tick)) {
if (ImGui::Checkbox("On/Off##Beat Tick", &config.sound.beat_tick)) {
toggle_beat_ticks();
}
ImGui::BeginDisabled(not beat_tick);
{
auto volume = beat_ticks->get_volume();
if (ImGui::SliderInt("Volume##Beat Tick", &volume, 0, 10)) {
beat_ticks->set_volume(volume);
}
if (not config.sound.beat_tick) {
ImGui::BeginDisabled();
}
if (ImGui::SliderInt("Volume##Beat Tick", &config.sound.beat_tick_volume, 0, 10)) {
beat_ticks->set_volume(config.sound.beat_tick_volume);
}
if (not config.sound.beat_tick) {
ImGui::EndDisabled();
}
ImGui::EndDisabled();
ImGui::TreePop();
}
if (ImGui::TreeNodeEx("Note Clap", ImGuiTreeNodeFlags_DefaultOpen)) {
bool note_clap = note_claps_are_on();
if (ImGui::Checkbox("On/Off##Note Clap", &note_clap)) {
if (ImGui::Checkbox("On/Off##Note Clap", &config.sound.note_clap)) {
toggle_note_claps();
}
ImGui::BeginDisabled(not note_clap);
{
auto volume = note_claps->get_volume();
if (ImGui::SliderInt("Volume##Note Clap", &volume, 0, 10)) {
note_claps->set_volume(volume);
chord_claps->set_volume(volume);
}
if (ImGui::TreeNode("Advanced##Note Clap")) {
bool long_end = get_clap_on_long_note_ends();
if (ImGui::Checkbox("Clap on long note ends", &long_end)) {
toggle_clap_on_long_note_ends();
}
bool chord_clap = get_distinct_chord_claps();
if (ImGui::Checkbox("Distinct chord clap", &chord_clap)) {
toggle_distinct_chord_claps();
}
ImGui::TreePop();
}
if (not config.sound.note_clap) {
ImGui::BeginDisabled();
}
if (ImGui::SliderInt("Volume##Note Clap", &config.sound.note_clap_volume, 0, 10)) {
note_claps->set_volume(config.sound.note_clap_volume);
chord_claps->set_volume(config.sound.note_clap_volume);
}
if (ImGui::TreeNode("Advanced##Note Clap")) {
if (ImGui::Checkbox("Clap on long note ends", &config.sound.clap_on_long_note_ends)) {
toggle_clap_on_long_note_ends();
}
if (ImGui::Checkbox("Distinct chord clap", &config.sound.distinct_chord_clap)) {
toggle_distinct_chord_claps();
}
ImGui::TreePop();
}
if (not config.sound.note_clap) {
ImGui::EndDisabled();
}
ImGui::EndDisabled();
ImGui::TreePop();
}
}
@ -1535,6 +1530,15 @@ void EditorState::replace_applicable_timing_with(const better::Timing& new_timin
}
}
bool EditorState::note_clap_stream_is_on() const {
return audio.contains_stream(note_clap_stream) or audio.contains_stream(chord_clap_stream);
}
bool EditorState::beat_tick_stream_is_on() const {
return audio.contains_stream(beat_tick_stream);
}
Interval<sf::Time> EditorState::choose_editable_range() {
Interval<sf::Time> new_range{sf::Time::Zero, sf::Time::Zero};
// In all cases, allow editing from beat zero (which might be at a negative
@ -1599,6 +1603,7 @@ std::optional<std::filesystem::path> EditorState::full_audio_path() {
* Reloads music from what's indicated in the "music path" field of the song
* Resets the music state in case anything fails
* Updates playbackPosition and preview_end as well
* Sets claps and ticks according to config
*/
void EditorState::reload_music() {
const auto status_before = get_status();
@ -1620,6 +1625,22 @@ void EditorState::reload_music() {
if (music.has_value()) {
audio.add_stream(music_stream, {*music, false});
}
set_volume(config.sound.music_volume);
if (config.sound.beat_tick != beat_tick_stream_is_on()) {
toggle_beat_ticks();
}
beat_ticks->set_volume(config.sound.beat_tick_volume);
if (config.sound.note_clap != note_clap_stream_is_on()) {
toggle_note_claps();
}
note_claps->set_volume(config.sound.note_clap_volume);
chord_claps->set_volume(config.sound.note_clap_volume);
if (config.sound.clap_on_long_note_ends != note_claps->does_play_long_note_ends()) {
toggle_clap_on_long_note_ends();
}
if (config.sound.distinct_chord_clap != not note_claps->does_play_chords()) {
toggle_distinct_chord_claps();
}
pause();
set_playback_position(current_time());
switch (status_before) {

View File

@ -79,8 +79,6 @@ public:
std::future<std::vector<TempoCandidate>> tempo_candidates_loader;
std::optional<std::vector<TempoCandidate>> tempo_candidates;
int get_volume() const;
void set_volume(int newMusicVolume);
void volume_up();
@ -118,13 +116,9 @@ public:
bool has_any_audio() const;
void toggle_playback();
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;};
void toggle_beat_ticks();
bool beat_ticks_are_on() const {return audio.contains_stream(beat_tick_stream);};
void play();
void pause();
void stop();
@ -253,11 +247,11 @@ public:
private:
int volume = 10; // 0 -> 10
int speed = 10; // 1 -> 20
bool clap_on_long_note_ends = false;
bool distinct_chord_clap = false;
bool note_clap_stream_is_on() const;
bool beat_tick_stream_is_on() const;
// Playback status used when there is no actual audio being played
sf::SoundSource::Status status = sf::SoundSource::Stopped;