diff --git a/CHANGELOG.md b/CHANGELOG.md index b158f23..de8aaa3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/config.cpp b/src/config.cpp index 77433fb..ec7d115 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -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(); + if (sound_table["music_volume"].is_integer()) { + const auto val = sound_table["music_volume"].value(); + music_volume = std::clamp(*val, 0, 10); + } + if (sound_table["beat_tick"].is_boolean()) { + beat_tick = *sound_table["beat_tick"].value(); + } + if (sound_table["beat_tick_volume"].is_integer()) { + const auto val = sound_table["beat_tick_volume"].value(); + beat_tick_volume = std::clamp(*val, 0, 10); + } + if (sound_table["note_clap"].is_boolean()) { + note_clap = *sound_table["note_clap"].value(); + } + if (sound_table["note_clap_volume"].is_integer()) { + const auto val = sound_table["note_clap_volume"].value(); + 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(); + } + if (sound_table["distinct_chord_clap"].is_boolean()) { + distinct_chord_clap = *sound_table["distinct_chord_clap"].value(); + } +} + +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); } \ No newline at end of file diff --git a/src/config.hpp b/src/config.hpp index 1e55320..b777ae1 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -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); diff --git a/src/editor_state.cpp b/src/editor_state.cpp index 3141dc4..63f78c6 100644 --- a/src/editor_state.cpp +++ b/src/editor_state.cpp @@ -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 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", ¬e_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 EditorState::choose_editable_range() { Interval 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 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) { diff --git a/src/editor_state.hpp b/src/editor_state.hpp index f605461..56383d4 100644 --- a/src/editor_state.hpp +++ b/src/editor_state.hpp @@ -79,8 +79,6 @@ public: std::future> tempo_candidates_loader; std::optional> 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;