More things working with claps

This commit is contained in:
Stepland 2022-06-09 00:28:21 +02:00
parent d22d80a3e5
commit b6e8d2b622
8 changed files with 53 additions and 70 deletions

View File

@ -2,7 +2,7 @@ sources += files([
'al_check.cpp', 'al_check.cpp',
'al_resource.cpp', 'al_resource.cpp',
'audio_device.cpp', 'audio_device.cpp',
'clap_player.cpp', 'note_claps.cpp',
'open_music.cpp', 'open_music.cpp',
'open_sound_stream.cpp', 'open_sound_stream.cpp',
'precise_sound_stream.cpp', 'precise_sound_stream.cpp',

View File

@ -1,36 +1,32 @@
#include "clap_player.hpp" #include "note_claps.hpp"
#include <memory> #include <memory>
#include <stdexcept> #include <stdexcept>
#include "../better_note.hpp" #include "../better_note.hpp"
ClapPlayer::ClapPlayer( 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
) : ) :
notes(notes_), notes(notes_),
timing(timing_), timing(timing_),
note_clap(), note_clap()
chord_clap()
{ {
if (not note_clap.loadFromFile(assets / "sounds" / "note.wav")) { if (not note_clap.loadFromFile(assets / "sounds" / "note.wav")) {
throw std::runtime_error("Could not load note clap audio file"); throw std::runtime_error("Could not load note clap audio file");
} }
if (not chord_clap.loadFromFile(assets / "sounds" / "chord.wav")) {
throw std::runtime_error("Could not load chord clap audio file");
}
sf::SoundStream::initialize(note_clap.getChannelCount(), note_clap.getSampleRate()); sf::SoundStream::initialize(note_clap.getChannelCount(), note_clap.getSampleRate());
samples.resize(note_clap.getChannelCount() * note_clap.getSampleRate(), 0); samples.resize(note_clap.getChannelCount() * note_clap.getSampleRate(), 0);
} }
void ClapPlayer::set_notes_and_timing(const better::Notes* notes_, const better::Timing* timing_) { void NoteClaps::set_notes_and_timing(const better::Notes* notes_, const better::Timing* timing_) {
notes = notes_; notes = notes_;
timing = timing_; timing = timing_;
} }
bool ClapPlayer::onGetData(sf::SoundStream::Chunk& data) { bool NoteClaps::onGetData(sf::SoundStream::Chunk& data) {
samples.assign(samples.size(), 0); samples.assign(samples.size(), 0);
if (timing != nullptr and notes != nullptr) { if (timing != nullptr and notes != nullptr) {
const auto start_sample = current_sample; const auto start_sample = current_sample;
@ -51,7 +47,7 @@ bool ClapPlayer::onGetData(sf::SoundStream::Chunk& data) {
// Should we still be playing the clap ? // Should we still be playing the clap ?
const auto next = std::next(it); const auto next = std::next(it);
const auto last_audible_start = start_sample - static_cast<std::int64_t>(note_clap.getSampleCount()); const auto last_audible_start = start_sample - static_cast<std::int64_t>(note_clap.getSampleCount());
if (it->first <= last_audible_start) { if (it->first <= last_audible_start or ((not play_chords) and it->second > 1)) {
it = notes_at_sample.erase(it); it = notes_at_sample.erase(it);
} else { } else {
const auto full_clap_start_in_buffer = static_cast<std::int64_t>(it->first) - static_cast<std::int64_t>(start_sample); const auto full_clap_start_in_buffer = static_cast<std::int64_t>(it->first) - static_cast<std::int64_t>(start_sample);
@ -92,12 +88,12 @@ bool ClapPlayer::onGetData(sf::SoundStream::Chunk& data) {
return true; return true;
}; };
void ClapPlayer::onSeek(sf::Time timeOffset) { void NoteClaps::onSeek(sf::Time timeOffset) {
current_sample = timeToSamples(timeOffset); current_sample = timeToSamples(timeOffset);
notes_at_sample.clear(); notes_at_sample.clear();
}; };
std::int64_t ClapPlayer::timeToSamples(sf::Time position) const { std::int64_t NoteClaps::timeToSamples(sf::Time position) const {
// Always ROUND, no unchecked truncation, hence the addition in the numerator. // Always ROUND, no unchecked truncation, hence the addition in the numerator.
// This avoids most precision errors arising from "samples => Time => samples" conversions // This avoids most precision errors arising from "samples => Time => samples" conversions
// Original rounding calculation is ((Micros * Freq * Channels) / 1000000) + 0.5 // Original rounding calculation is ((Micros * Freq * Channels) / 1000000) + 0.5
@ -105,7 +101,7 @@ std::int64_t ClapPlayer::timeToSamples(sf::Time position) const {
return ((static_cast<std::int64_t>(position.asMicroseconds()) * note_clap.getSampleRate() * note_clap.getChannelCount()) + 500000) / 1000000; return ((static_cast<std::int64_t>(position.asMicroseconds()) * note_clap.getSampleRate() * note_clap.getChannelCount()) + 500000) / 1000000;
} }
sf::Time ClapPlayer::samplesToTime(std::int64_t samples) const { sf::Time NoteClaps::samplesToTime(std::int64_t samples) const {
sf::Time position = sf::Time::Zero; sf::Time position = sf::Time::Zero;
// Make sure we don't divide by 0 // Make sure we don't divide by 0

View File

@ -10,15 +10,16 @@
#include "../better_timing.hpp" #include "../better_timing.hpp"
#include "precise_sound_stream.hpp" #include "precise_sound_stream.hpp"
class ClapPlayer: public PreciseSoundStream { class NoteClaps: public PreciseSoundStream {
public: public:
ClapPlayer( 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
); );
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::atomic<bool> play_chords = true;
protected: protected:
bool onGetData(Chunk& data) override; bool onGetData(Chunk& data) override;
@ -35,5 +36,4 @@ private:
const better::Notes* notes; const better::Notes* notes;
const better::Timing* timing; const better::Timing* timing;
sf::SoundBuffer note_clap; sf::SoundBuffer note_clap;
sf::SoundBuffer chord_clap;
}; };

View File

@ -83,10 +83,10 @@ void SyncedSoundStreams::unsafe_update_streams() {
bool modified_stuff = false; bool modified_stuff = false;
auto _do_request = VariantVisitor { auto _do_request = VariantVisitor {
[this](const AddStream& a) { [this](const AddStream& a) {
InternalStream internal_stream{a.s, {}}; InternalStream internal_stream{a.stream, {}};
internal_stream.buffers.m_channelCount = a.s->getChannelCount(); internal_stream.buffers.m_channelCount = a.stream->getChannelCount();
internal_stream.buffers.m_sampleRate = a.s->getSampleRate(); internal_stream.buffers.m_sampleRate = a.stream->getSampleRate();
internal_stream.buffers.m_format = AudioDevice::getFormatFromChannelCount(a.s->getChannelCount()); internal_stream.buffers.m_format = AudioDevice::getFormatFromChannelCount(a.stream->getChannelCount());
streams.emplace(a.name, internal_stream); streams.emplace(a.name, internal_stream);
}, },
[this](const RemoveStream& r) { [this](const RemoveStream& r) {

View File

@ -49,7 +49,7 @@ struct InternalStream {
struct AddStream { struct AddStream {
std::string name; std::string name;
std::shared_ptr<PreciseSoundStream> s; std::shared_ptr<PreciseSoundStream> stream;
}; };
struct RemoveStream { struct RemoveStream {

View File

@ -35,7 +35,7 @@
#include "variant_visitor.hpp" #include "variant_visitor.hpp"
EditorState::EditorState(const std::filesystem::path& assets_) : EditorState::EditorState(const std::filesystem::path& assets_) :
clap_player(std::make_shared<ClapPlayer>(nullptr, nullptr, assets_)), note_claps(std::make_shared<NoteClaps>(nullptr, nullptr, assets_)),
playfield(assets_), playfield(assets_),
linear_view(assets_), linear_view(assets_),
applicable_timing(song.timing), applicable_timing(song.timing),
@ -43,7 +43,7 @@ EditorState::EditorState(const std::filesystem::path& assets_) :
{ {
reload_music(); reload_music();
reload_jacket(); reload_jacket();
audio.add_stream(note_clap_stream, clap_player); audio.add_stream(note_clap_stream, note_claps);
}; };
EditorState::EditorState( EditorState::EditorState(
@ -53,7 +53,7 @@ EditorState::EditorState(
) : ) :
song(song_), song(song_),
song_path(song_path), song_path(song_path),
clap_player(std::make_shared<ClapPlayer>(nullptr, nullptr, assets_)), note_claps(std::make_shared<NoteClaps>(nullptr, nullptr, assets_)),
playfield(assets_), playfield(assets_),
linear_view(assets_), linear_view(assets_),
applicable_timing(song.timing), applicable_timing(song.timing),
@ -65,7 +65,7 @@ EditorState::EditorState(
} }
reload_music(); reload_music();
reload_jacket(); reload_jacket();
audio.add_stream(note_clap_stream, clap_player); audio.add_stream(note_clap_stream, note_claps);
}; };
int EditorState::get_volume() const { int EditorState::get_volume() const {
@ -110,6 +110,14 @@ const Interval<sf::Time>& EditorState::get_editable_range() {
return editable_range; return editable_range;
}; };
void EditorState::toggle_playback() {
if (get_status() != sf::SoundSource::Playing) {
play();
} else {
pause();
}
}
void EditorState::play() { void EditorState::play() {
audio.play(); audio.play();
} }
@ -795,7 +803,7 @@ void EditorState::open_chart(const std::string& name) {
chart_state.emplace(chart, name_ref, assets); chart_state.emplace(chart, name_ref, assets);
reload_editable_range(); reload_editable_range();
reload_applicable_timing(); reload_applicable_timing();
clap_player->set_notes_and_timing(&chart.notes, &applicable_timing); note_claps->set_notes_and_timing(&chart.notes, &applicable_timing);
}; };
void EditorState::update_visible_notes() { void EditorState::update_visible_notes() {

View File

@ -8,7 +8,7 @@
#include <SFML/Graphics.hpp> #include <SFML/Graphics.hpp>
#include "custom_sfml_audio/clap_player.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"
#include "widgets/linear_view.hpp" #include "widgets/linear_view.hpp"
@ -46,7 +46,7 @@ public:
std::optional<ChartState> chart_state; std::optional<ChartState> chart_state;
SyncedSoundStreams audio; SyncedSoundStreams audio;
std::shared_ptr<ClapPlayer> clap_player; std::shared_ptr<NoteClaps> note_claps;
std::optional<std::shared_ptr<OpenMusic>> music = {}; std::optional<std::shared_ptr<OpenMusic>> music = {};
int get_volume() const; int get_volume() const;
@ -76,6 +76,7 @@ public:
const Interval<sf::Time>& get_editable_range(); const Interval<sf::Time>& get_editable_range();
void toggle_playback();
void play(); void play();
void pause(); void pause();
void stop(); void stop();

View File

@ -1,3 +1,4 @@
#include <SFML/Audio/SoundSource.hpp>
#include <string> #include <string>
#include <filesystem> #include <filesystem>
#include <variant> #include <variant>
@ -63,9 +64,9 @@ int main() {
16.f); 16.f);
ImGui::SFML::UpdateFontTexture(); ImGui::SFML::UpdateFontTexture();
SoundEffect beatTick {assets_folder / "sounds" / "beat.wav"}; // SoundEffect beatTick {assets_folder / "sounds" / "beat.wav"};
SoundEffect noteTick {assets_folder / "sounds" / "note.wav"}; // SoundEffect noteTick {assets_folder / "sounds" / "note.wav"};
SoundEffect chordTick {assets_folder / "sounds" / "chord.wav"}; // SoundEffect chordTick {assets_folder / "sounds" / "chord.wav"};
// Loading markers preview // Loading markers preview
std::map<std::filesystem::path, sf::Texture> markerPreviews; std::map<std::filesystem::path, sf::Texture> markerPreviews;
@ -297,6 +298,7 @@ int main() {
* F keys * F keys
*/ */
case sf::Keyboard::F3: case sf::Keyboard::F3:
/*
if (beatTick.toggle()) { if (beatTick.toggle()) {
notificationsQueue.push(std::make_shared<TextNotification>( notificationsQueue.push(std::make_shared<TextNotification>(
"Beat tick : on")); "Beat tick : on"));
@ -304,8 +306,10 @@ int main() {
notificationsQueue.push(std::make_shared<TextNotification>( notificationsQueue.push(std::make_shared<TextNotification>(
"Beat tick : off")); "Beat tick : off"));
} }
*/
break; break;
case sf::Keyboard::F4: case sf::Keyboard::F4:
/*
if (event.key.shift) { if (event.key.shift) {
if (chordTick.toggle()) { if (chordTick.toggle()) {
noteTick.shouldPlay = true; noteTick.shouldPlay = true;
@ -325,11 +329,12 @@ int main() {
"Note tick : off")); "Note tick : off"));
} }
} }
*/
break; break;
case sf::Keyboard::Space: case sf::Keyboard::Space:
if (not ImGui::GetIO().WantTextInput) { if (not ImGui::GetIO().WantTextInput) {
if (editor_state) { if (editor_state) {
editor_state->playing = not editor_state->playing; editor_state->toggle_playback();
} }
} }
break; break;
@ -416,36 +421,15 @@ int main() {
// Audio playback management // Audio playback management
if (editor_state) { if (editor_state) {
editor_state->update_visible_notes(); editor_state->update_visible_notes();
if (editor_state->playing) { if (editor_state->get_status() == sf::SoundSource::Playing) {
editor_state->previous_playback_position = editor_state->playback_position; editor_state->previous_playback_position = editor_state->playback_position;
editor_state->playback_position = editor_state->current_time() + delta * (editor_state->get_speed() / 10.f);
switch (editor_state->get_status()) {
case sf::Music::Stopped:
case sf::Music::Paused:
editor_state->set_playback_position(editor_state->current_time());
editor_state->play();
break;
case sf::Music::Playing:
editor_state->playback_position = editor_state->get_precise_playback_position(); editor_state->playback_position = editor_state->get_precise_playback_position();
break; // editor_state->playback_position = editor_state->current_time() + delta * (editor_state->get_speed() / 10.f);
default:
break;
}
if (beatTick.shouldPlay) {
const auto previous_beat = editor_state->previous_exact_beats();
const auto current_beat = editor_state->current_exact_beats();
if (floor_fraction(previous_beat) != floor_fraction(current_beat)) {
beatTick.play();
}
}
if (editor_state->current_time() > editor_state->get_editable_range().end) { if (editor_state->current_time() > editor_state->get_editable_range().end) {
editor_state->playing = false;
editor_state->playback_position = editor_state->get_editable_range().end;
}
} else if (editor_state->get_status() == sf::SoundSource::Playing) {
editor_state->pause(); editor_state->pause();
} }
} }
}
// Drawing // Drawing
if (editor_state) { if (editor_state) {
@ -495,24 +479,18 @@ int main() {
} else { } else {
chartPropertiesDialog.should_refresh_values = true; chartPropertiesDialog.should_refresh_values = true;
} }
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); {
{ if (ImGui::TreeNode("Note Clap")) {
if (ImGui::TreeNode("Beat Tick")) { static auto play_chords = editor_state->note_claps->play_chords.load();
beatTick.displayControls(); if (ImGui::Checkbox("Play on chords", &play_chords)) {
ImGui::TreePop(); editor_state->note_claps->play_chords.store(play_chords);
} }
if (ImGui::TreeNode("Note Tick")) {
noteTick.displayControls();
ImGui::Checkbox("Chord sound", &chordTick.shouldPlay);
ImGui::TreePop(); ImGui::TreePop();
} }
} }
ImGui::End(); ImGui::End();
} }
} else { } else {
bg.render(window); bg.render(window);
} }