mirror of
https://gitlab.com/square-game-liberation-front/F.E.I.S.git
synced 2025-02-28 15:30:32 +01:00
Slowly getting there but we still have some bugs
This commit is contained in:
parent
68e6eaff32
commit
beaea91380
90
src/custom_sfml_audio/chord_claps.cpp
Normal file
90
src/custom_sfml_audio/chord_claps.cpp
Normal 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();
|
||||
};
|
41
src/custom_sfml_audio/chord_claps.hpp
Normal file
41
src/custom_sfml_audio/chord_claps.hpp
Normal 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;
|
||||
};
|
@ -3,6 +3,7 @@ sources += files([
|
||||
'al_resource.cpp',
|
||||
'audio_device.cpp',
|
||||
'beat_ticks.cpp',
|
||||
'chord_claps.cpp',
|
||||
'fake_pitched_sound_stream.cpp',
|
||||
'note_claps.cpp',
|
||||
'open_music.cpp',
|
||||
|
@ -9,14 +9,19 @@
|
||||
#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"
|
||||
|
||||
NoteClaps::NoteClaps(
|
||||
const better::Notes* notes_,
|
||||
const better::Timing* timing_,
|
||||
const std::filesystem::path& assets,
|
||||
float pitch_
|
||||
float pitch_,
|
||||
bool play_chords_,
|
||||
bool play_long_note_ends_
|
||||
) :
|
||||
FakePitchedSoundStream(assets / "sounds" / "note.wav", pitch_),
|
||||
play_chords(play_chords_),
|
||||
play_long_note_ends(play_long_note_ends_),
|
||||
notes(notes_),
|
||||
timing(timing_)
|
||||
{}
|
||||
@ -25,9 +30,13 @@ NoteClaps::NoteClaps(
|
||||
const better::Notes* notes_,
|
||||
const better::Timing* timing_,
|
||||
std::shared_ptr<sf::SoundBuffer> note_clap,
|
||||
float pitch
|
||||
float pitch,
|
||||
bool play_chords_,
|
||||
bool play_long_note_ends_
|
||||
) :
|
||||
FakePitchedSoundStream(note_clap, pitch),
|
||||
play_chords(play_chords_),
|
||||
play_long_note_ends(play_long_note_ends_),
|
||||
notes(notes_),
|
||||
timing(timing_)
|
||||
{}
|
||||
@ -37,12 +46,51 @@ void NoteClaps::set_notes_and_timing(const better::Notes* notes_, const better::
|
||||
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>(
|
||||
notes,
|
||||
timing,
|
||||
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 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;
|
||||
}
|
||||
};
|
||||
|
||||
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){
|
||||
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 sample = static_cast<std::int64_t>(music_time_to_samples(time));
|
||||
// interval_tree::in is inclusive of the upper bound but here we
|
||||
// don't want claps that *start* at the end sample since
|
||||
// it's an *exculsive* end
|
||||
if (sample < absolute_buffer_end) {
|
||||
notes_at_sample.insert(sample);
|
||||
}
|
||||
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(
|
||||
sample,
|
||||
output_buffer,
|
||||
|
@ -17,26 +17,43 @@ public:
|
||||
const better::Notes* notes_,
|
||||
const better::Timing* timing_,
|
||||
const std::filesystem::path& assets,
|
||||
float pitch_
|
||||
float pitch_,
|
||||
bool play_chords = true,
|
||||
bool play_long_note_ends = false
|
||||
);
|
||||
|
||||
NoteClaps(
|
||||
const better::Notes* notes_,
|
||||
const better::Timing* timing_,
|
||||
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);
|
||||
|
||||
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:
|
||||
bool onGetData(Chunk& data) override;
|
||||
void onSeek(sf::Time timeOffset) override;
|
||||
|
||||
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::Timing* timing;
|
||||
|
@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <span>
|
||||
|
||||
#include <SFML/Audio/SoundBuffer.hpp>
|
||||
@ -12,4 +14,66 @@ void copy_sample_at_points(
|
||||
std::span<sf::Int16> output_buffer,
|
||||
std::set<std::int64_t>& starting_points,
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <boost/math/constants/constants.hpp>
|
||||
#include <cassert>
|
||||
#include <initializer_list>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
@ -64,25 +65,30 @@ SyncedSoundStreams::~SyncedSoundStreams() {
|
||||
|
||||
void SyncedSoundStreams::change_streams(std::function<void()> callback) {
|
||||
const auto oldStatus = getStatus();
|
||||
pause();
|
||||
setPitch(pitch);
|
||||
const auto position = getPlayingOffset();
|
||||
stop();
|
||||
|
||||
callback();
|
||||
|
||||
reload_sources();
|
||||
setPitch(pitch);
|
||||
setPlayingOffset(position);
|
||||
if (oldStatus == sf::SoundSource::Playing) {
|
||||
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([&](){
|
||||
for (const auto& [name, new_stream] : new_streams) {
|
||||
if (contains_stream(name)) {
|
||||
remove_stream_internal(name);
|
||||
}
|
||||
for (const auto& name : to_remove) {
|
||||
remove_stream_internal(name);
|
||||
}
|
||||
for (const auto& [name, new_stream] : to_add) {
|
||||
remove_stream_internal(name);
|
||||
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) {
|
||||
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_sampleRate = s.stream->getSampleRate();
|
||||
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);
|
||||
}
|
||||
|
||||
bool SyncedSoundStreams::contains_stream(const std::string& name) {
|
||||
bool SyncedSoundStreams::contains_stream(const std::string& name) const {
|
||||
return streams.contains(name);
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,7 @@ struct Buffers {
|
||||
|
||||
struct NewStream {
|
||||
std::shared_ptr<PreciseSoundStream> stream;
|
||||
bool reconstruct_on_pitch_change;
|
||||
bool bypasses_openal_pitch;
|
||||
};
|
||||
|
||||
struct InternalStream {
|
||||
@ -57,10 +57,13 @@ public:
|
||||
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 remove_stream(const std::string& name);
|
||||
bool contains_stream(const std::string& name);
|
||||
bool contains_stream(const std::string& name) const;
|
||||
|
||||
void play();
|
||||
void pause();
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <imgui.h>
|
||||
#include <imgui_internal.h>
|
||||
#include <imgui_stdlib.h>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <nowide/fstream.hpp>
|
||||
#include <sstream>
|
||||
@ -36,6 +37,7 @@
|
||||
|
||||
EditorState::EditorState(const std::filesystem::path& assets_) :
|
||||
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)),
|
||||
playfield(assets_),
|
||||
linear_view(assets_),
|
||||
@ -55,6 +57,7 @@ EditorState::EditorState(
|
||||
song(song_),
|
||||
song_path(song_path),
|
||||
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)),
|
||||
playfield(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() {
|
||||
if (audio.contains_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);
|
||||
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
|
||||
// the internal call to setPlaybackPosition
|
||||
audio.setPitch(pitch);
|
||||
@ -829,6 +890,7 @@ void EditorState::open_chart(const std::string& name) {
|
||||
reload_editable_range();
|
||||
reload_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);
|
||||
};
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
|
||||
#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/open_music.hpp"
|
||||
#include "custom_sfml_audio/synced_sound_streams.hpp"
|
||||
@ -25,7 +26,8 @@
|
||||
|
||||
|
||||
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";
|
||||
|
||||
/*
|
||||
@ -49,6 +51,7 @@ public:
|
||||
|
||||
SyncedSoundStreams audio;
|
||||
std::shared_ptr<NoteClaps> note_claps;
|
||||
std::shared_ptr<ChordClaps> chord_claps;
|
||||
std::shared_ptr<BeatTicks> beat_ticks;
|
||||
std::optional<std::shared_ptr<OpenMusic>> music = {};
|
||||
|
||||
@ -80,7 +83,14 @@ public:
|
||||
const Interval<sf::Time>& get_editable_range();
|
||||
|
||||
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();
|
||||
@ -172,6 +182,9 @@ private:
|
||||
int volume = 10; // 0 -> 10
|
||||
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
|
||||
(and maybe editable) from the editor, can extend before and after
|
||||
|
21
src/main.cpp
21
src/main.cpp
@ -481,10 +481,27 @@ int main() {
|
||||
}
|
||||
if (editor_state->showSoundSettings) {
|
||||
ImGui::Begin("Sound Settings", &editor_state->showSoundSettings, ImGuiWindowFlags_AlwaysAutoResize); {
|
||||
ImGui::Text("Beat Tick");
|
||||
if (ImGui::Button("Toggle")) {
|
||||
bool beat_tick = editor_state->beat_ticks_are_on();
|
||||
if (ImGui::Checkbox("beat tick", &beat_tick)) {
|
||||
editor_state->toggle_beat_ticks();
|
||||
}
|
||||
bool note_clap = editor_state->note_claps_are_on();
|
||||
if (ImGui::Checkbox("note clap", ¬e_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();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user