From e9bfb69d361de9da082f304e695742a01e8289d8 Mon Sep 17 00:00:00 2001 From: Stepland <10530295-Buggyroom@users.noreply.gitlab.com> Date: Thu, 29 Sep 2022 23:44:38 +0200 Subject: [PATCH] Trunc down reported buffer size to closest multiple of the number of channels, otherwise openAL complains about the buffer size and makes the stream stop early --- src/custom_sfml_audio/beat_ticks.cpp | 2 +- src/custom_sfml_audio/note_claps.cpp | 38 +++++++---- src/custom_sfml_audio/note_claps.hpp | 9 ++- .../precise_sound_stream.hpp | 5 ++ .../synced_sound_streams.cpp | 66 +++++++++---------- .../synced_sound_streams.hpp | 13 +--- src/main.cpp | 5 -- 7 files changed, 72 insertions(+), 66 deletions(-) diff --git a/src/custom_sfml_audio/beat_ticks.cpp b/src/custom_sfml_audio/beat_ticks.cpp index b4b6c11..db27d58 100644 --- a/src/custom_sfml_audio/beat_ticks.cpp +++ b/src/custom_sfml_audio/beat_ticks.cpp @@ -17,7 +17,7 @@ BeatTicks::BeatTicks( throw std::runtime_error("Could not load beat tick audio file"); } sf::SoundStream::initialize(beat_tick.getChannelCount(), beat_tick.getSampleRate()); - samples.resize(timeToSamples(sf::milliseconds(200)), 0); + samples.resize(timeToSamples(sf::seconds(1)), 0); } void BeatTicks::set_timing(const better::Timing* timing_) { diff --git a/src/custom_sfml_audio/note_claps.cpp b/src/custom_sfml_audio/note_claps.cpp index d67c519..ef2d49c 100644 --- a/src/custom_sfml_audio/note_claps.cpp +++ b/src/custom_sfml_audio/note_claps.cpp @@ -1,5 +1,6 @@ #include "note_claps.hpp" +#include #include #include #include @@ -13,13 +14,26 @@ NoteClaps::NoteClaps( ) : notes(notes_), timing(timing_), - note_clap() + note_clap(std::make_shared()) { - 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"); } - sf::SoundStream::initialize(note_clap.getChannelCount(), note_clap.getSampleRate()); - samples.resize(timeToSamples(sf::milliseconds(200)), 0); + sf::SoundStream::initialize(note_clap->getChannelCount(), note_clap->getSampleRate()); + samples.resize(timeToSamples(sf::seconds(1)), 0); +} + +NoteClaps::NoteClaps( + const better::Notes* notes_, + const better::Timing* timing_, + std::shared_ptr note_clap_ +) : + notes(notes_), + timing(timing_), + note_clap(note_clap_) +{ + sf::SoundStream::initialize(note_clap->getChannelCount(), note_clap->getSampleRate()); + samples.resize(timeToSamples(sf::seconds(1)), 0); } void NoteClaps::set_notes_and_timing(const better::Notes* notes_, const better::Timing* timing_) { @@ -47,13 +61,13 @@ bool NoteClaps::onGetData(sf::SoundStream::Chunk& data) { for (auto it = notes_at_sample.begin(); it != notes_at_sample.end();) { // Should we still be playing the clap ? const auto next = std::next(it); - const auto last_audible_start = start_sample - static_cast(note_clap.getSampleCount()); - if (it->first <= last_audible_start or ((not play_chords) and it->second > 1)) { + const auto last_audible_start = start_sample - static_cast(note_clap->getSampleCount()); + if (it->first <= last_audible_start) { it = notes_at_sample.erase(it); } else { const auto full_clap_start_in_buffer = static_cast(it->first) - static_cast(start_sample); const auto slice_start_in_buffer = std::max(std::int64_t(0), full_clap_start_in_buffer); - const auto full_clap_end_in_buffer = full_clap_start_in_buffer + static_cast(note_clap.getSampleCount()); + const auto full_clap_end_in_buffer = full_clap_start_in_buffer + static_cast(note_clap->getSampleCount()); auto slice_end_in_buffer = full_clap_end_in_buffer; bool clap_finished_playing_in_current_buffer = true; if (next != notes_at_sample.end()) { @@ -68,9 +82,9 @@ bool NoteClaps::onGetData(sf::SoundStream::Chunk& data) { auto slice_start_in_clap = slice_start_in_buffer - full_clap_start_in_buffer; auto slice_size = std::min( slice_end_in_buffer - slice_start_in_buffer, - static_cast(note_clap.getSampleCount()) - slice_start_in_clap + static_cast(note_clap->getSampleCount()) - slice_start_in_clap ); - const auto clap_pointer = note_clap.getSamples() + slice_start_in_clap; + const auto clap_pointer = note_clap->getSamples() + slice_start_in_clap; std::copy( clap_pointer, clap_pointer + slice_size, @@ -102,15 +116,15 @@ std::int64_t NoteClaps::timeToSamples(sf::Time position) const { // This avoids most precision errors arising from "samples => Time => samples" conversions // Original rounding calculation is ((Micros * Freq * Channels) / 1000000) + 0.5 // We refactor it to keep Int64 as the data type throughout the whole operation. - return ((static_cast(position.asMicroseconds()) * note_clap.getSampleRate() * note_clap.getChannelCount()) + 500000) / 1000000; + return ((static_cast(position.asMicroseconds()) * note_clap->getSampleRate() * note_clap->getChannelCount()) + 500000) / 1000000; } sf::Time NoteClaps::samplesToTime(std::int64_t samples) const { sf::Time position = sf::Time::Zero; // Make sure we don't divide by 0 - if (note_clap.getSampleRate() != 0 && note_clap.getChannelCount() != 0) - position = sf::microseconds((samples * 1000000) / (note_clap.getChannelCount() * note_clap.getSampleRate())); + if (note_clap->getSampleRate() != 0 && note_clap->getChannelCount() != 0) + position = sf::microseconds((samples * 1000000) / (note_clap->getChannelCount() * note_clap->getSampleRate())); return position; } \ No newline at end of file diff --git a/src/custom_sfml_audio/note_claps.hpp b/src/custom_sfml_audio/note_claps.hpp index 9ec7eeb..7e0ec79 100644 --- a/src/custom_sfml_audio/note_claps.hpp +++ b/src/custom_sfml_audio/note_claps.hpp @@ -18,8 +18,13 @@ public: const std::filesystem::path& assets ); + NoteClaps( + const better::Notes* notes_, + const better::Timing* timing_, + std::shared_ptr note_clap_ + ); + void set_notes_and_timing(const better::Notes* notes, const better::Timing* timing); - std::atomic play_chords = true; protected: bool onGetData(Chunk& data) override; @@ -35,5 +40,5 @@ private: const better::Notes* notes; const better::Timing* timing; - sf::SoundBuffer note_clap; + std::shared_ptr note_clap; }; \ No newline at end of file diff --git a/src/custom_sfml_audio/precise_sound_stream.hpp b/src/custom_sfml_audio/precise_sound_stream.hpp index 262c1f9..6f7c30f 100644 --- a/src/custom_sfml_audio/precise_sound_stream.hpp +++ b/src/custom_sfml_audio/precise_sound_stream.hpp @@ -16,4 +16,9 @@ struct PreciseSoundStream : public OpenSoundStream { LPALGETSOURCEDVSOFT alGetSourcedvSOFT; std::array alSecOffsetLatencySoft() const; sf::Time lag = sf::Time::Zero; +}; + +template +sf::Uint64 timeToSamples(sf::Time position, T sample_rate, T channel_count) { + return ((static_cast(position.asMicroseconds()) * sample_rate * channel_count) + 500000) / 1000000; }; \ No newline at end of file diff --git a/src/custom_sfml_audio/synced_sound_streams.cpp b/src/custom_sfml_audio/synced_sound_streams.cpp index ea40bfe..1b2bf1c 100644 --- a/src/custom_sfml_audio/synced_sound_streams.cpp +++ b/src/custom_sfml_audio/synced_sound_streams.cpp @@ -59,44 +59,38 @@ SyncedSoundStreams::~SyncedSoundStreams() { awaitStreamingThread(); } - -void SyncedSoundStreams::add_stream(const std::string& name, std::shared_ptr s) { +void SyncedSoundStreams::change_streams(std::function callback) { const auto oldStatus = getStatus(); - const auto position = getPrecisePlayingOffset(); + const auto position = getPlayingOffset(); stop(); - { - InternalStream internal_stream{s, {}}; - internal_stream.buffers.m_channelCount = s->getChannelCount(); - internal_stream.buffers.m_sampleRate = s->getSampleRate(); - internal_stream.buffers.m_format = AudioDevice::getFormatFromChannelCount(s->getChannelCount()); - streams.emplace(name, internal_stream); - } + + callback(); + reload_sources(); - if (oldStatus != sf::SoundSource::Stopped) { - setPlayingOffset(position); - } + setPlayingOffset(position); if (oldStatus == sf::SoundSource::Playing) { play(); } } + +void SyncedSoundStreams::add_stream(const std::string& name, std::shared_ptr s) { + change_streams([&](){ + InternalStream internal_stream{s, {}}; + internal_stream.buffers.m_channelCount = s->getChannelCount(); + internal_stream.buffers.m_sampleRate = s->getSampleRate(); + internal_stream.buffers.m_format = AudioDevice::getFormatFromChannelCount(s->getChannelCount()); + streams.emplace(name, internal_stream); + }); +} + void SyncedSoundStreams::remove_stream(const std::string& name) { - const auto oldStatus = getStatus(); - const auto position = getPrecisePlayingOffset(); - stop(); - { - if (streams.contains(name)) { - streams.at(name).clear_queue(); - } - streams.erase(name); - } - reload_sources(); - if (oldStatus != sf::SoundSource::Stopped) { - setPlayingOffset(position); - } - if (oldStatus == sf::SoundSource::Playing) { - play(); - } + change_streams([&](){ + if (streams.contains(name)) { + streams.at(name).clear_queue(); + } + streams.erase(name); + }); } bool SyncedSoundStreams::contains_stream(const std::string& name) { @@ -204,10 +198,7 @@ void SyncedSoundStreams::setPlayingOffset(sf::Time timeOffset) { for (auto& [_, s]: streams) { s.stream->public_seek_callback(timeOffset); // Restart streaming - s.buffers.m_samplesProcessed = static_cast( - timeOffset.asSeconds() - * static_cast(s.buffers.m_sampleRate) - ) * s.buffers.m_channelCount; + s.buffers.m_samplesProcessed = timeToSamples(timeOffset, s.buffers.m_sampleRate, s.buffers.m_channelCount); } if (oldStatus == sf::SoundSource::Stopped) { @@ -380,7 +371,7 @@ void SyncedSoundStreams::streamData() { } // Fill it and push it back into the playing queue - if (!requestStop) { + if (not requestStop) { if (fillAndPushBuffer(s, bufferNum)) { requestStop = true; } @@ -459,10 +450,15 @@ bool SyncedSoundStreams::fillAndPushBuffer(InternalStream& stream, unsigned int } // Fill the buffer if some data was returned - if (data.samples && data.sampleCount) { + if (data.samples != nullptr && data.sampleCount != 0) { unsigned int buffer = stream.buffers.m_buffers[bufferNum]; // Fill the buffer + // Stepland : I don't know why, sometimes data.sampleCount is not a + // multiple of the number of channels which makes OpenAL error out on + // the alBufferData call, as a safety measure I trunc it down to the + // nearest multiple + data.sampleCount -= data.sampleCount % stream.buffers.m_channelCount; auto size = static_cast(data.sampleCount * sizeof(sf::Int16)); alCheck(alBufferData(buffer, stream.buffers.m_format, data.samples, size, static_cast(stream.buffers.m_sampleRate))); diff --git a/src/custom_sfml_audio/synced_sound_streams.hpp b/src/custom_sfml_audio/synced_sound_streams.hpp index 4bf5e2b..d6f31cb 100644 --- a/src/custom_sfml_audio/synced_sound_streams.hpp +++ b/src/custom_sfml_audio/synced_sound_streams.hpp @@ -42,21 +42,11 @@ struct Buffers { struct InternalStream { std::shared_ptr stream; Buffers buffers; + bool reconstruct_on_pitch_change; void clear_queue(); }; -struct AddStream { - std::string name; - std::shared_ptr stream; -}; - -struct RemoveStream { - std::string name; -}; - -using ChangeStreamsCommand = std::variant; - class SyncedSoundStreams : public AlResource { public: SyncedSoundStreams(); @@ -85,6 +75,7 @@ protected: void setProcessingInterval(sf::Time interval); private: + void change_streams(std::function callback); void streamData(); [[nodiscard]] bool fillAndPushBuffer(InternalStream& stream, unsigned int bufferNum, bool immediateLoop = false); [[nodiscard]] bool fillQueues(); diff --git a/src/main.cpp b/src/main.cpp index 7864101..7ecffc2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -481,11 +481,6 @@ int main() { } if (editor_state->showSoundSettings) { ImGui::Begin("Sound Settings", &editor_state->showSoundSettings, ImGuiWindowFlags_AlwaysAutoResize); { - ImGui::Text("Note Clap"); - static auto play_chords = editor_state->note_claps->play_chords.load(); - if (ImGui::Checkbox("Play on chords", &play_chords)) { - editor_state->note_claps->play_chords.store(play_chords); - } ImGui::Text("Beat Tick"); if (ImGui::Button("Toggle")) { editor_state->toggle_beat_ticks();