diff --git a/src/custom_sfml_audio/sampler_callback.cpp b/src/custom_sfml_audio/sampler_callback.cpp index 1135c84..635c2be 100644 --- a/src/custom_sfml_audio/sampler_callback.cpp +++ b/src/custom_sfml_audio/sampler_callback.cpp @@ -2,60 +2,84 @@ #include +std::optional compute_sample_slice( + const std::int64_t buffer_start, + const std::int64_t buffer_size, + const std::int64_t sample_start, + const std::int64_t sample_size, + const std::optional next_sample_start +) { + const auto buffer_end = buffer_start + buffer_size; + const auto sample_end = sample_start + sample_size; + const auto sample_deoverlapped_end = [&](){ + if (next_sample_start.has_value()) { + return std::min(*next_sample_start, sample_end); + } else { + return sample_end; + } + }(); + const auto sample_slice_start = std::max(sample_start, buffer_start); + const auto sample_slice_end = std::min(sample_deoverlapped_end, buffer_end); + const auto slice_size = sample_slice_end - sample_slice_start; + const auto slice_start_relative_to_sample_start = sample_slice_start - sample_start; + const auto slice_start_relative_to_buffer_start = sample_slice_start - buffer_start; + // all the possible error cases I could think of + if ( + sample_deoverlapped_end <= buffer_start + or sample_start >= buffer_end + or slice_size <= 0 + ) { + return {}; + } else { + return Slice{ + .start_in_sample=slice_start_relative_to_sample_start, + .size=slice_size, + .start_in_buffer=slice_start_relative_to_buffer_start, + .ends_in_this_buffer=sample_deoverlapped_end <= buffer_end + }; + } +} + void copy_sample_at_points( const std::shared_ptr& sample, std::span output_buffer, std::set& starting_points, - std::int64_t absolute_buffer_start + std::int64_t buffer_start ) { std::ranges::fill(output_buffer, 0); for (auto it = starting_points.begin(); it != starting_points.end();) { - const auto absolute_sample_start = *it; - const auto absolute_buffer_end = absolute_buffer_start + static_cast(output_buffer.size()); - const auto absolute_sample_end = absolute_sample_start + static_cast(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; - } else { - return INT64_MAX; - } - }(it) + const auto next_sample_start = [&]() -> std::optional { + const auto next = std::next(it); + if (next == starting_points.end()) { + return {}; + } else { + return *next; + } + }(); + const auto slice = compute_sample_slice( + buffer_start, + static_cast(output_buffer.size()), + *it, + static_cast(sample->getSampleCount()), + next_sample_start ); - 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 case I could think of - if ( - absolute_sample_deoverlapped_end <= absolute_buffer_start - or absolute_sample_start >= absolute_buffer_end - or slice_size <= 0 - ) { + + // bounds checking failed somehow + if (not slice) { 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; + const auto input_start = sample->getSamples() + slice->start_in_sample; + const auto input_end = input_start + slice->size; + const auto output_start = output_buffer.begin() + slice->start_in_buffer; 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) { + if (slice->ends_in_this_buffer) { it = starting_points.erase(it); } else { ++it; diff --git a/src/custom_sfml_audio/sampler_callback.hpp b/src/custom_sfml_audio/sampler_callback.hpp index 31bfabb..ae134af 100644 --- a/src/custom_sfml_audio/sampler_callback.hpp +++ b/src/custom_sfml_audio/sampler_callback.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -10,6 +11,22 @@ #include #include "fake_pitched_sound_stream.hpp" +#include "fmt/core.h" + +struct Slice { + std::int64_t start_in_sample; + std::int64_t size; + std::int64_t start_in_buffer; + bool ends_in_this_buffer; +}; + +std::optional compute_sample_slice( + const std::int64_t buffer_start, + const std::int64_t buffer_size, + const std::int64_t sample_start, + const std::int64_t sample_size, + const std::optional next_sample_start +); void copy_sample_at_points( const std::shared_ptr& sample, @@ -23,56 +40,42 @@ void copy_sample_at_points( const std::shared_ptr& sample, std::span output_buffer, std::map& starting_points, - std::int64_t absolute_buffer_start + std::int64_t 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(output_buffer.size()); - const auto absolute_sample_end = absolute_sample_start + static_cast(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 INT64_MAX; - } - }(it) + const auto next_sample_start = [&]() -> std::optional { + const auto next = std::next(it); + if (next == starting_points.end()) { + return {}; + } else { + return next->first; + } + }(); + const auto slice = compute_sample_slice( + buffer_start, + static_cast(output_buffer.size()), + it->first, + static_cast(sample->getSampleCount()), + next_sample_start ); - 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 - ) { + + // bounds checking failed somehow + if (not slice) { 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; + const auto input_start = sample->getSamples() + slice->start_in_sample; + const auto input_end = input_start + slice->size; + const auto output_start = output_buffer.begin() + slice->start_in_buffer; 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) { + if (slice->ends_in_this_buffer) { it = starting_points.erase(it); } else { ++it;