mirror of
https://gitlab.com/square-game-liberation-front/F.E.I.S.git
synced 2025-03-01 07:50:25 +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',
|
'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',
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
21
src/main.cpp
21
src/main.cpp
@ -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", ¬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();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user