Slowly getting there but we still have some bugs

This commit is contained in:
Stepland 2022-10-07 01:47:45 +02:00
parent 68e6eaff32
commit beaea91380
11 changed files with 409 additions and 35 deletions

View File

@ -0,0 +1,90 @@
#include "chord_claps.hpp"
#include <SFML/Audio/SoundBuffer.hpp>
#include <SFML/System/Time.hpp>
#include <limits>
#include <memory>
#include <stdexcept>
#include "../better_note.hpp"
#include "src/custom_sfml_audio/fake_pitched_sound_stream.hpp"
#include "src/custom_sfml_audio/sampler_callback.hpp"
#include "src/special_numeric_types.hpp"
ChordClaps::ChordClaps(
const better::Notes* notes_,
const better::Timing* timing_,
const std::filesystem::path& assets,
float pitch_
) :
FakePitchedSoundStream(assets / "sounds" / "chord.wav", pitch_),
notes(notes_),
timing(timing_)
{}
ChordClaps::ChordClaps(
const better::Notes* notes_,
const better::Timing* timing_,
std::shared_ptr<sf::SoundBuffer> note_clap,
float pitch
) :
FakePitchedSoundStream(note_clap, pitch),
notes(notes_),
timing(timing_)
{}
void ChordClaps::set_notes_and_timing(const better::Notes* notes_, const better::Timing* timing_) {
notes = notes_;
timing = timing_;
}
std::shared_ptr<ChordClaps> ChordClaps::with_pitch(float new_pitch) {
return std::make_shared<ChordClaps>(
notes,
timing,
sample,
new_pitch
);
}
bool ChordClaps::onGetData(sf::SoundStream::Chunk& data) {
if (timing != nullptr and notes != nullptr) {
const auto absolute_buffer_start = first_sample_of_next_buffer;
const std::int64_t absolute_buffer_end = first_sample_of_next_buffer + static_cast<std::int64_t>(output_buffer.size());
const auto start_time = samples_to_music_time(absolute_buffer_start);
const auto end_time = samples_to_music_time(absolute_buffer_end);
const auto start_beat = timing->beats_at(start_time);
const auto end_beat = timing->beats_at(end_time);
const auto count_clap_at = [&](const Fraction beat){
const auto time = timing->time_at(beat);
const auto sample = static_cast<std::int64_t>(music_time_to_samples(time));
// we don't want claps that *start* at the end sample since
// absolute_buffer_end is an *exculsive* end
if (sample < absolute_buffer_end) {
notes_at_sample[sample] += 1;
}
};
notes->in(start_beat, end_beat, [&](const better::Notes::const_iterator& it){
count_clap_at(it->second.get_time());
});
std::erase_if(notes_at_sample, [](const auto& it){return it.second <= 1;});
copy_sample_at_points(
sample,
output_buffer,
notes_at_sample,
absolute_buffer_start
);
}
data.samples = output_buffer.data();
data.sampleCount = output_buffer.size();
first_sample_of_next_buffer += output_buffer.size();
return true;
};
void ChordClaps::onSeek(sf::Time timeOffset) {
first_sample_of_next_buffer = music_time_to_samples(timeOffset);
notes_at_sample.clear();
};

View File

@ -0,0 +1,41 @@
#pragma once
#include <map>
#include <memory>
#include <SFML/Audio/SoundBuffer.hpp>
#include "../better_notes.hpp"
#include "../better_timing.hpp"
#include "fake_pitched_sound_stream.hpp"
class ChordClaps: public FakePitchedSoundStream {
public:
ChordClaps(
const better::Notes* notes_,
const better::Timing* timing_,
const std::filesystem::path& assets,
float pitch_
);
ChordClaps(
const better::Notes* notes_,
const better::Timing* timing_,
std::shared_ptr<sf::SoundBuffer> note_clap_,
float pitch_
);
void set_notes_and_timing(const better::Notes* notes, const better::Timing* timing);
std::shared_ptr<ChordClaps> with_pitch(float pitch);
protected:
bool onGetData(Chunk& data) override;
void onSeek(sf::Time timeOffset) override;
private:
std::map<std::int64_t, unsigned int> notes_at_sample;
const better::Notes* notes;
const better::Timing* timing;
};

View File

@ -3,6 +3,7 @@ sources += files([
'al_resource.cpp', 'al_resource.cpp',
'audio_device.cpp', 'audio_device.cpp',
'beat_ticks.cpp', 'beat_ticks.cpp',
'chord_claps.cpp',
'fake_pitched_sound_stream.cpp', 'fake_pitched_sound_stream.cpp',
'note_claps.cpp', 'note_claps.cpp',
'open_music.cpp', 'open_music.cpp',

View File

@ -9,14 +9,19 @@
#include "../better_note.hpp" #include "../better_note.hpp"
#include "src/custom_sfml_audio/fake_pitched_sound_stream.hpp" #include "src/custom_sfml_audio/fake_pitched_sound_stream.hpp"
#include "src/custom_sfml_audio/sampler_callback.hpp" #include "src/custom_sfml_audio/sampler_callback.hpp"
#include "src/special_numeric_types.hpp"
NoteClaps::NoteClaps( NoteClaps::NoteClaps(
const better::Notes* notes_, const better::Notes* notes_,
const better::Timing* timing_, const better::Timing* timing_,
const std::filesystem::path& assets, const std::filesystem::path& assets,
float pitch_ float pitch_,
bool play_chords_,
bool play_long_note_ends_
) : ) :
FakePitchedSoundStream(assets / "sounds" / "note.wav", pitch_), FakePitchedSoundStream(assets / "sounds" / "note.wav", pitch_),
play_chords(play_chords_),
play_long_note_ends(play_long_note_ends_),
notes(notes_), notes(notes_),
timing(timing_) timing(timing_)
{} {}
@ -25,9 +30,13 @@ NoteClaps::NoteClaps(
const better::Notes* notes_, const better::Notes* notes_,
const better::Timing* timing_, const better::Timing* timing_,
std::shared_ptr<sf::SoundBuffer> note_clap, std::shared_ptr<sf::SoundBuffer> note_clap,
float pitch float pitch,
bool play_chords_,
bool play_long_note_ends_
) : ) :
FakePitchedSoundStream(note_clap, pitch), FakePitchedSoundStream(note_clap, pitch),
play_chords(play_chords_),
play_long_note_ends(play_long_note_ends_),
notes(notes_), notes(notes_),
timing(timing_) timing(timing_)
{} {}
@ -37,12 +46,51 @@ void NoteClaps::set_notes_and_timing(const better::Notes* notes_, const better::
timing = timing_; timing = timing_;
} }
std::shared_ptr<NoteClaps> NoteClaps::with_pitch(float pitch) { std::shared_ptr<NoteClaps> NoteClaps::with_pitch(float new_pitch) {
return std::make_shared<NoteClaps>( return std::make_shared<NoteClaps>(
notes, notes,
timing, timing,
sample, sample,
pitch new_pitch,
play_chords,
play_long_note_ends
);
}
std::shared_ptr<NoteClaps> NoteClaps::with_chords(bool new_play_chords) {
return std::make_shared<NoteClaps>(
notes,
timing,
sample,
pitch,
new_play_chords,
play_long_note_ends
);
}
std::shared_ptr<NoteClaps> NoteClaps::with_long_note_ends(bool new_play_long_note_ends) {
return std::make_shared<NoteClaps>(
notes,
timing,
sample,
pitch,
play_chords,
new_play_long_note_ends
);
}
std::shared_ptr<NoteClaps> NoteClaps::with(
float pitch_,
bool play_chords_,
bool play_long_note_ends_
) {
return std::make_shared<NoteClaps>(
notes,
timing,
sample,
pitch_,
play_chords_,
play_long_note_ends_
); );
} }
@ -54,22 +102,34 @@ bool NoteClaps::onGetData(sf::SoundStream::Chunk& data) {
const auto end_time = samples_to_music_time(absolute_buffer_end); const auto end_time = samples_to_music_time(absolute_buffer_end);
const auto start_beat = timing->beats_at(start_time); const auto start_beat = timing->beats_at(start_time);
const auto end_beat = timing->beats_at(end_time); const auto end_beat = timing->beats_at(end_time);
const auto count_clap_at = [&](const Fraction beat){
notes->in(start_beat, end_beat, [&](const better::Notes::const_iterator& it){
const auto beat = it->second.get_time();
// ignore long notes that started before the current buffer
if (beat < start_beat) {
return;
}
const auto time = timing->time_at(beat); const auto time = timing->time_at(beat);
const auto sample = static_cast<std::int64_t>(music_time_to_samples(time)); const auto sample = static_cast<std::int64_t>(music_time_to_samples(time));
// interval_tree::in is inclusive of the upper bound but here we // we don't want claps that *start* at the end sample since
// don't want claps that *start* at the end sample since // absolute_buffer_end is an *exculsive* end
// it's an *exculsive* end
if (sample < absolute_buffer_end) { if (sample < absolute_buffer_end) {
notes_at_sample.insert(sample); notes_at_sample[sample] += 1;
} }
};
const auto add_claps_of_note = VariantVisitor {
[&](const better::TapNote& t) {
count_clap_at(t.get_time());
},
[&](const better::LongNote& l) {
count_clap_at(l.get_time());
if (play_long_note_ends) {
count_clap_at(l.get_end());
}
},
};
notes->in(start_beat, end_beat, [&](const better::Notes::const_iterator& it){
it->second.visit(add_claps_of_note);
}); });
if (not play_chords) {
std::erase_if(notes_at_sample, [](const auto& it){return it.second > 1;});
}
copy_sample_at_points( copy_sample_at_points(
sample, sample,
output_buffer, output_buffer,

View File

@ -17,26 +17,43 @@ public:
const better::Notes* notes_, const better::Notes* notes_,
const better::Timing* timing_, const better::Timing* timing_,
const std::filesystem::path& assets, const std::filesystem::path& assets,
float pitch_ float pitch_,
bool play_chords = true,
bool play_long_note_ends = false
); );
NoteClaps( NoteClaps(
const better::Notes* notes_, const better::Notes* notes_,
const better::Timing* timing_, const better::Timing* timing_,
std::shared_ptr<sf::SoundBuffer> note_clap_, std::shared_ptr<sf::SoundBuffer> note_clap_,
float time_factor_ float pitch_,
bool play_chords = true,
bool play_long_note_ends = false
); );
void set_notes_and_timing(const better::Notes* notes, const better::Timing* timing); void set_notes_and_timing(const better::Notes* notes, const better::Timing* timing);
std::shared_ptr<NoteClaps> with_pitch(float pitch); std::shared_ptr<NoteClaps> with_pitch(float pitch);
bool does_play_chords() const {return play_chords;};
std::shared_ptr<NoteClaps> with_chords(bool play_chords);
bool does_play_long_note_ends() const {return play_long_note_ends;};
std::shared_ptr<NoteClaps> with_long_note_ends(bool play_long_note_ends);
std::shared_ptr<NoteClaps> with(
float pitch,
bool play_chords,
bool play_long_note_ends
);
protected: protected:
bool onGetData(Chunk& data) override; bool onGetData(Chunk& data) override;
void onSeek(sf::Time timeOffset) override; void onSeek(sf::Time timeOffset) override;
private: private:
std::set<std::int64_t> notes_at_sample; std::map<std::int64_t, unsigned int> notes_at_sample;
bool play_chords = true;
bool play_long_note_ends = false;
const better::Notes* notes; const better::Notes* notes;
const better::Timing* timing; const better::Timing* timing;

View File

@ -1,7 +1,9 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <map>
#include <memory> #include <memory>
#include <set>
#include <span> #include <span>
#include <SFML/Audio/SoundBuffer.hpp> #include <SFML/Audio/SoundBuffer.hpp>
@ -13,3 +15,65 @@ void copy_sample_at_points(
std::set<std::int64_t>& starting_points, std::set<std::int64_t>& starting_points,
std::int64_t absolute_buffer_start std::int64_t absolute_buffer_start
); );
template<class T>
void copy_sample_at_points(
const std::shared_ptr<sf::SoundBuffer>& sample,
std::span<sf::Int16> output_buffer,
std::map<std::int64_t, T>& starting_points,
std::int64_t absolute_buffer_start
) {
std::ranges::fill(output_buffer, 0);
for (auto it = starting_points.begin(); it != starting_points.end();) {
const auto absolute_sample_start = it->first;
const auto absolute_buffer_end = absolute_buffer_start + static_cast<std::int64_t>(output_buffer.size());
const auto absolute_sample_end = absolute_sample_start + static_cast<std::int64_t>(sample->getSampleCount());
const auto absolute_sample_deoverlapped_end = std::min(
absolute_sample_end,
[&](const auto& it){
const auto next = std::next(it);
if (next != starting_points.end()) {
return next->first;
} else {
return std::numeric_limits<std::int64_t>::max();
}
}(it)
);
const auto absolute_sample_slice_start = std::max(
absolute_sample_start,
absolute_buffer_start
);
const auto absolute_sample_slice_end = std::min(
absolute_sample_deoverlapped_end,
absolute_buffer_end
);
const auto slice_size = absolute_sample_slice_end - absolute_sample_slice_start;
const auto slice_start_relative_to_sample_start = absolute_sample_slice_start - absolute_sample_start;
const auto slice_start_relative_to_buffer_start = absolute_sample_slice_start - absolute_buffer_start;
// Exit early in all the possible error cases I could think of
if (
absolute_sample_deoverlapped_end <= absolute_buffer_start
or absolute_sample_start >= absolute_buffer_end
or slice_size <= 0
) {
it = starting_points.erase(it);
continue;
}
const auto input_start = sample->getSamples() + slice_start_relative_to_sample_start;
const auto input_end = input_start + slice_size;
const auto output_start = output_buffer.begin() + slice_start_relative_to_buffer_start;
std::copy(
input_start,
input_end,
output_start
);
// has this sample been fully played in this buffer ?
if (absolute_sample_deoverlapped_end <= absolute_buffer_end) {
it = starting_points.erase(it);
} else {
++it;
}
}
}

View File

@ -2,6 +2,7 @@
#include <boost/math/constants/constants.hpp> #include <boost/math/constants/constants.hpp>
#include <cassert> #include <cassert>
#include <initializer_list>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
@ -64,25 +65,30 @@ SyncedSoundStreams::~SyncedSoundStreams() {
void SyncedSoundStreams::change_streams(std::function<void()> callback) { void SyncedSoundStreams::change_streams(std::function<void()> callback) {
const auto oldStatus = getStatus(); const auto oldStatus = getStatus();
pause();
setPitch(pitch);
const auto position = getPlayingOffset(); const auto position = getPlayingOffset();
stop(); stop();
callback(); callback();
reload_sources(); reload_sources();
setPitch(pitch);
setPlayingOffset(position); setPlayingOffset(position);
if (oldStatus == sf::SoundSource::Playing) { if (oldStatus == sf::SoundSource::Playing) {
play(); play();
} }
} }
void SyncedSoundStreams::update_streams(std::map<std::string, NewStream> new_streams) { void SyncedSoundStreams::update_streams(
const std::map<std::string, NewStream>& to_add,
const std::initializer_list<std::string>& to_remove
) {
change_streams([&](){ change_streams([&](){
for (const auto& [name, new_stream] : new_streams) { for (const auto& name : to_remove) {
if (contains_stream(name)) { remove_stream_internal(name);
remove_stream_internal(name); }
} for (const auto& [name, new_stream] : to_add) {
remove_stream_internal(name);
add_stream_internal(name, new_stream); add_stream_internal(name, new_stream);
} }
}); });
@ -96,7 +102,7 @@ void SyncedSoundStreams::add_stream(const std::string& name, NewStream s) {
} }
void SyncedSoundStreams::add_stream_internal(const std::string& name, NewStream s) { void SyncedSoundStreams::add_stream_internal(const std::string& name, NewStream s) {
InternalStream internal_stream{s.stream, {}, s.reconstruct_on_pitch_change}; InternalStream internal_stream{s.stream, {}, s.bypasses_openal_pitch};
internal_stream.buffers.m_channelCount = s.stream->getChannelCount(); internal_stream.buffers.m_channelCount = s.stream->getChannelCount();
internal_stream.buffers.m_sampleRate = s.stream->getSampleRate(); internal_stream.buffers.m_sampleRate = s.stream->getSampleRate();
internal_stream.buffers.m_format = AudioDevice::getFormatFromChannelCount(s.stream->getChannelCount()); internal_stream.buffers.m_format = AudioDevice::getFormatFromChannelCount(s.stream->getChannelCount());
@ -116,7 +122,7 @@ void SyncedSoundStreams::remove_stream_internal(const std::string& name) {
streams.erase(name); streams.erase(name);
} }
bool SyncedSoundStreams::contains_stream(const std::string& name) { bool SyncedSoundStreams::contains_stream(const std::string& name) const {
return streams.contains(name); return streams.contains(name);
} }

View File

@ -41,7 +41,7 @@ struct Buffers {
struct NewStream { struct NewStream {
std::shared_ptr<PreciseSoundStream> stream; std::shared_ptr<PreciseSoundStream> stream;
bool reconstruct_on_pitch_change; bool bypasses_openal_pitch;
}; };
struct InternalStream { struct InternalStream {
@ -57,10 +57,13 @@ public:
SyncedSoundStreams(); SyncedSoundStreams();
~SyncedSoundStreams(); ~SyncedSoundStreams();
void update_streams(std::map<std::string, NewStream> new_streams); void update_streams(
const std::map<std::string, NewStream>& to_add,
const std::initializer_list<std::string>& to_remove = {}
);
void add_stream(const std::string& name, NewStream s); void add_stream(const std::string& name, NewStream s);
void remove_stream(const std::string& name); void remove_stream(const std::string& name);
bool contains_stream(const std::string& name); bool contains_stream(const std::string& name) const;
void play(); void play();
void pause(); void pause();

View File

@ -12,6 +12,7 @@
#include <imgui.h> #include <imgui.h>
#include <imgui_internal.h> #include <imgui_internal.h>
#include <imgui_stdlib.h> #include <imgui_stdlib.h>
#include <initializer_list>
#include <memory> #include <memory>
#include <nowide/fstream.hpp> #include <nowide/fstream.hpp>
#include <sstream> #include <sstream>
@ -36,6 +37,7 @@
EditorState::EditorState(const std::filesystem::path& assets_) : EditorState::EditorState(const std::filesystem::path& assets_) :
note_claps(std::make_shared<NoteClaps>(nullptr, nullptr, assets_, 1.f)), note_claps(std::make_shared<NoteClaps>(nullptr, nullptr, assets_, 1.f)),
chord_claps(std::make_shared<ChordClaps>(nullptr, nullptr, assets_, 1.f)),
beat_ticks(std::make_shared<BeatTicks>(nullptr, assets_, 1.f)), beat_ticks(std::make_shared<BeatTicks>(nullptr, assets_, 1.f)),
playfield(assets_), playfield(assets_),
linear_view(assets_), linear_view(assets_),
@ -55,6 +57,7 @@ EditorState::EditorState(
song(song_), song(song_),
song_path(song_path), song_path(song_path),
note_claps(std::make_shared<NoteClaps>(nullptr, nullptr, assets_, 1.f)), note_claps(std::make_shared<NoteClaps>(nullptr, nullptr, assets_, 1.f)),
chord_claps(std::make_shared<ChordClaps>(nullptr, nullptr, assets_, 1.f)),
beat_ticks(std::make_shared<BeatTicks>(nullptr, assets_, 1.f)), beat_ticks(std::make_shared<BeatTicks>(nullptr, assets_, 1.f)),
playfield(assets_), playfield(assets_),
linear_view(assets_), linear_view(assets_),
@ -120,6 +123,60 @@ void EditorState::toggle_playback() {
} }
} }
void EditorState::toggle_note_claps() {
if (
audio.contains_stream(note_clap_stream)
or audio.contains_stream(chord_clap_stream)
) {
audio.update_streams({}, {note_clap_stream, chord_clap_stream});
} else {
note_claps = note_claps->with(
get_pitch(),
not distinct_chord_clap,
clap_on_long_note_ends
);
std::map<std::string, NewStream> streams = {{note_clap_stream, {note_claps, true}}};
if (distinct_chord_clap) {
chord_claps = chord_claps->with_pitch(get_pitch());
streams[chord_clap_stream] = {chord_claps, true};
}
audio.update_streams(streams);
}
}
void EditorState::toggle_clap_on_long_note_ends() {
clap_on_long_note_ends = not clap_on_long_note_ends;
note_claps = note_claps->with(
get_pitch(),
not distinct_chord_clap,
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(
get_pitch(),
not distinct_chord_clap,
clap_on_long_note_ends
);
if (distinct_chord_clap) {
chord_claps = chord_claps->with_pitch(get_pitch());
audio.update_streams(
{
{note_clap_stream, {note_claps, true}},
{chord_clap_stream, {chord_claps, true}}
}
);
} else {
audio.update_streams(
{{note_clap_stream, {note_claps, true}}},
{chord_clap_stream}
);
}
}
void EditorState::toggle_beat_ticks() { void EditorState::toggle_beat_ticks() {
if (audio.contains_stream(beat_tick_stream)) { if (audio.contains_stream(beat_tick_stream)) {
audio.remove_stream(beat_tick_stream); audio.remove_stream(beat_tick_stream);
@ -155,6 +212,10 @@ void EditorState::set_pitch(float pitch) {
beat_ticks = beat_ticks->with_pitch(pitch); beat_ticks = beat_ticks->with_pitch(pitch);
update[beat_tick_stream] = {beat_ticks, true}; update[beat_tick_stream] = {beat_ticks, true};
} }
if (audio.contains_stream(chord_clap_stream)) {
chord_claps = chord_claps->with_pitch(pitch);
update[chord_clap_stream] = {chord_claps, true};
}
// setPitch has to be called before update_streams to avoid problems in // setPitch has to be called before update_streams to avoid problems in
// the internal call to setPlaybackPosition // the internal call to setPlaybackPosition
audio.setPitch(pitch); audio.setPitch(pitch);
@ -829,6 +890,7 @@ void EditorState::open_chart(const std::string& name) {
reload_editable_range(); reload_editable_range();
reload_applicable_timing(); reload_applicable_timing();
note_claps->set_notes_and_timing(&chart.notes, &applicable_timing); note_claps->set_notes_and_timing(&chart.notes, &applicable_timing);
chord_claps->set_notes_and_timing(&chart.notes, &applicable_timing);
beat_ticks->set_timing(&applicable_timing); beat_ticks->set_timing(&applicable_timing);
}; };

View File

@ -9,6 +9,7 @@
#include "custom_sfml_audio/beat_ticks.hpp" #include "custom_sfml_audio/beat_ticks.hpp"
#include "custom_sfml_audio/chord_claps.hpp"
#include "custom_sfml_audio/note_claps.hpp" #include "custom_sfml_audio/note_claps.hpp"
#include "custom_sfml_audio/open_music.hpp" #include "custom_sfml_audio/open_music.hpp"
#include "custom_sfml_audio/synced_sound_streams.hpp" #include "custom_sfml_audio/synced_sound_streams.hpp"
@ -25,7 +26,8 @@
const std::string music_stream = "music"; const std::string music_stream = "music";
const std::string note_clap_stream = "aaa_note_clap"; const std::string note_clap_stream = "note_clap";
const std::string chord_clap_stream = "chord_clap";
const std::string beat_tick_stream = "beat_tick"; const std::string beat_tick_stream = "beat_tick";
/* /*
@ -49,6 +51,7 @@ public:
SyncedSoundStreams audio; SyncedSoundStreams audio;
std::shared_ptr<NoteClaps> note_claps; std::shared_ptr<NoteClaps> note_claps;
std::shared_ptr<ChordClaps> chord_claps;
std::shared_ptr<BeatTicks> beat_ticks; std::shared_ptr<BeatTicks> beat_ticks;
std::optional<std::shared_ptr<OpenMusic>> music = {}; std::optional<std::shared_ptr<OpenMusic>> music = {};
@ -80,7 +83,14 @@ public:
const Interval<sf::Time>& get_editable_range(); const Interval<sf::Time>& get_editable_range();
void toggle_playback(); 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(); void toggle_beat_ticks();
bool beat_ticks_are_on() const {return audio.contains_stream(beat_tick_stream);};
void play(); void play();
void pause(); void pause();
void stop(); void stop();
@ -172,6 +182,9 @@ private:
int volume = 10; // 0 -> 10 int volume = 10; // 0 -> 10
int speed = 10; // 1 -> 20 int speed = 10; // 1 -> 20
bool clap_on_long_note_ends = false;
bool distinct_chord_clap = false;
/* /*
sf::Time bounds (in the audio file "coordinates") which are accessible sf::Time bounds (in the audio file "coordinates") which are accessible
(and maybe editable) from the editor, can extend before and after (and maybe editable) from the editor, can extend before and after

View File

@ -481,10 +481,27 @@ int main() {
} }
if (editor_state->showSoundSettings) { if (editor_state->showSoundSettings) {
ImGui::Begin("Sound Settings", &editor_state->showSoundSettings, ImGuiWindowFlags_AlwaysAutoResize); { ImGui::Begin("Sound Settings", &editor_state->showSoundSettings, ImGuiWindowFlags_AlwaysAutoResize); {
ImGui::Text("Beat Tick"); bool beat_tick = editor_state->beat_ticks_are_on();
if (ImGui::Button("Toggle")) { if (ImGui::Checkbox("beat tick", &beat_tick)) {
editor_state->toggle_beat_ticks(); editor_state->toggle_beat_ticks();
} }
bool note_clap = editor_state->note_claps_are_on();
if (ImGui::Checkbox("note clap", &note_clap)) {
editor_state->toggle_note_claps();
}
ImGui::BeginDisabled(not note_clap); {
ImGui::Indent();
bool long_end = editor_state->get_clap_on_long_note_ends();
if (ImGui::Checkbox("clap on long note ends", &long_end)) {
editor_state->toggle_clap_on_long_note_ends();
}
bool chord_clap = editor_state->get_distinct_chord_claps();
if (ImGui::Checkbox("distinct chord clap", &chord_clap)) {
editor_state->toggle_distinct_chord_claps();
}
ImGui::Unindent();
} ImGui::EndDisabled();
} }
ImGui::End(); ImGui::End();
} }