mirror of
https://gitlab.com/square-game-liberation-front/F.E.I.S.git
synced 2025-02-28 15:30:32 +01:00
Refactor claps and ticks a bit
This commit is contained in:
parent
ce5f162b25
commit
54aef06e04
@ -5,35 +5,25 @@
|
||||
#include <stdexcept>
|
||||
|
||||
#include "../better_note.hpp"
|
||||
#include "sampler_callback.hpp"
|
||||
|
||||
BeatTicks::BeatTicks(
|
||||
const better::Timing* timing_,
|
||||
const std::filesystem::path& assets,
|
||||
float pitch_
|
||||
float pitch
|
||||
) :
|
||||
pitch(pitch_),
|
||||
timing(timing_),
|
||||
beat_tick(std::make_shared<sf::SoundBuffer>())
|
||||
{
|
||||
if (not beat_tick->loadFromFile(assets / "sounds" / "beat.wav")) {
|
||||
throw std::runtime_error("Could not load beat tick audio file");
|
||||
}
|
||||
sf::SoundStream::initialize(beat_tick->getChannelCount(), beat_tick->getSampleRate());
|
||||
samples.resize(timeToSamples(sf::seconds(1)), 0);
|
||||
}
|
||||
FakePitchedSoundStream(assets / "sounds" / "beat.wav", pitch),
|
||||
timing(timing_)
|
||||
{}
|
||||
|
||||
BeatTicks::BeatTicks(
|
||||
const better::Timing* timing_,
|
||||
std::shared_ptr<sf::SoundBuffer> beat_tick_,
|
||||
float pitch_
|
||||
std::shared_ptr<sf::SoundBuffer> beat_tick,
|
||||
float pitch
|
||||
) :
|
||||
pitch(pitch_),
|
||||
timing(timing_),
|
||||
beat_tick(beat_tick_)
|
||||
{
|
||||
sf::SoundStream::initialize(beat_tick_->getChannelCount(), beat_tick_->getSampleRate());
|
||||
samples.resize(timeToSamples(sf::seconds(1)), 0);
|
||||
}
|
||||
FakePitchedSoundStream(beat_tick, pitch),
|
||||
timing(timing_)
|
||||
{}
|
||||
|
||||
void BeatTicks::set_timing(const better::Timing* timing_) {
|
||||
timing = timing_;
|
||||
@ -42,18 +32,17 @@ void BeatTicks::set_timing(const better::Timing* timing_) {
|
||||
std::shared_ptr<BeatTicks> BeatTicks::with_pitch(float pitch) {
|
||||
return std::make_shared<BeatTicks>(
|
||||
timing,
|
||||
beat_tick,
|
||||
sample,
|
||||
pitch
|
||||
);
|
||||
}
|
||||
|
||||
bool BeatTicks::onGetData(sf::SoundStream::Chunk& data) {
|
||||
samples.assign(samples.size(), 0);
|
||||
if (timing != nullptr) {
|
||||
const auto start_sample = current_sample;
|
||||
const auto end_sample = current_sample + static_cast<std::int64_t>(samples.size());
|
||||
const auto start_time = samplesToTime(start_sample);
|
||||
const auto end_time = samplesToTime(end_sample);
|
||||
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);
|
||||
|
||||
@ -63,77 +52,26 @@ bool BeatTicks::onGetData(sf::SoundStream::Chunk& data) {
|
||||
}
|
||||
for (std::int64_t beat = first_beat; beat < end_beat; beat++) {
|
||||
const auto time = timing->time_at(beat);
|
||||
const auto sample = static_cast<std::int64_t>(timeToSamples(time));
|
||||
const auto sample = static_cast<std::int64_t>(music_time_to_samples(time));
|
||||
beat_at_sample.insert(sample);
|
||||
}
|
||||
|
||||
for (auto it = beat_at_sample.begin(); it != beat_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<std::int64_t>(beat_tick->getSampleCount());
|
||||
if (*it <= last_audible_start) {
|
||||
it = beat_at_sample.erase(it);
|
||||
} else {
|
||||
const auto full_tick_start_in_buffer = *it - static_cast<std::int64_t>(start_sample);
|
||||
const auto slice_start_in_buffer = std::max(std::int64_t(0), full_tick_start_in_buffer);
|
||||
const auto full_tick_end_in_buffer = full_tick_start_in_buffer + static_cast<std::int64_t>(beat_tick->getSampleCount());
|
||||
auto slice_end_in_buffer = full_tick_end_in_buffer;
|
||||
bool tick_finished_playing_in_current_buffer = true;
|
||||
if (next != beat_at_sample.end()) {
|
||||
slice_end_in_buffer = std::min(
|
||||
slice_end_in_buffer,
|
||||
*next - static_cast<std::int64_t>(start_sample)
|
||||
);
|
||||
} else if (slice_end_in_buffer > static_cast<std::int64_t>(samples.size())) {
|
||||
tick_finished_playing_in_current_buffer = false;
|
||||
slice_end_in_buffer = static_cast<std::int64_t>(samples.size());
|
||||
}
|
||||
auto slice_start_in_tick = slice_start_in_buffer - full_tick_start_in_buffer;
|
||||
auto slice_size = std::min(
|
||||
slice_end_in_buffer - slice_start_in_buffer,
|
||||
static_cast<std::int64_t>(beat_tick->getSampleCount()) - slice_start_in_tick
|
||||
);
|
||||
const auto tick_pointer = beat_tick->getSamples() + slice_start_in_tick;
|
||||
std::copy(
|
||||
tick_pointer,
|
||||
tick_pointer + slice_size,
|
||||
samples.begin() + slice_start_in_buffer
|
||||
);
|
||||
if (tick_finished_playing_in_current_buffer) {
|
||||
it = beat_at_sample.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
copy_sample_at_points(
|
||||
sample,
|
||||
output_buffer,
|
||||
beat_at_sample,
|
||||
absolute_buffer_start
|
||||
);
|
||||
}
|
||||
|
||||
data.samples = samples.data();
|
||||
data.sampleCount = samples.size();
|
||||
current_sample += samples.size();
|
||||
data.samples = output_buffer.data();
|
||||
data.sampleCount = output_buffer.size();
|
||||
first_sample_of_next_buffer += output_buffer.size();
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
void BeatTicks::onSeek(sf::Time timeOffset) {
|
||||
current_sample = timeToSamples(timeOffset);
|
||||
first_sample_of_next_buffer = music_time_to_samples(timeOffset);
|
||||
beat_at_sample.clear();
|
||||
};
|
||||
|
||||
std::int64_t BeatTicks::timeToSamples(sf::Time position) const {
|
||||
// Always ROUND, no unchecked truncation, hence the addition in the numerator.
|
||||
// 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<std::int64_t>((position / pitch).asMicroseconds()) * beat_tick->getSampleRate() * beat_tick->getChannelCount()) + 500000) / 1000000;
|
||||
}
|
||||
|
||||
sf::Time BeatTicks::samplesToTime(std::int64_t samples) const {
|
||||
sf::Time position = sf::Time::Zero;
|
||||
|
||||
// Make sure we don't divide by 0
|
||||
if (beat_tick->getSampleRate() != 0 && beat_tick->getChannelCount() != 0)
|
||||
position = sf::microseconds((samples * 1000000) / (beat_tick->getChannelCount() * beat_tick->getSampleRate()));
|
||||
|
||||
return position * pitch;
|
||||
}
|
||||
};
|
@ -6,9 +6,9 @@
|
||||
#include <SFML/Audio/SoundBuffer.hpp>
|
||||
|
||||
#include "../better_timing.hpp"
|
||||
#include "precise_sound_stream.hpp"
|
||||
#include "fake_pitched_sound_stream.hpp"
|
||||
|
||||
class BeatTicks: public PreciseSoundStream {
|
||||
class BeatTicks: public FakePitchedSoundStream {
|
||||
public:
|
||||
BeatTicks(
|
||||
const better::Timing* timing_,
|
||||
@ -31,14 +31,7 @@ protected:
|
||||
void onSeek(sf::Time timeOffset) override;
|
||||
|
||||
private:
|
||||
float pitch = 1.f;
|
||||
std::vector<sf::Int16> samples;
|
||||
std::int64_t current_sample = 0;
|
||||
std::int64_t timeToSamples(sf::Time position) const;
|
||||
sf::Time samplesToTime(std::int64_t samples) const;
|
||||
|
||||
std::set<std::int64_t> beat_at_sample;
|
||||
|
||||
const better::Timing* timing;
|
||||
std::shared_ptr<sf::SoundBuffer> beat_tick;
|
||||
};
|
60
src/custom_sfml_audio/fake_pitched_sound_stream.cpp
Normal file
60
src/custom_sfml_audio/fake_pitched_sound_stream.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
#include "fake_pitched_sound_stream.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <SFML/Audio/SoundBuffer.hpp>
|
||||
|
||||
FakePitchedSoundStream::FakePitchedSoundStream(
|
||||
const std::filesystem::path& path_to_sample,
|
||||
float pitch_
|
||||
) :
|
||||
pitch(pitch_),
|
||||
sample(std::make_shared<sf::SoundBuffer>())
|
||||
{
|
||||
if (not sample->loadFromFile(path_to_sample)) {
|
||||
throw std::runtime_error(fmt::format("Could not load audio sample : {}", path_to_sample.string()));
|
||||
}
|
||||
finish_initializing_the_sample();
|
||||
}
|
||||
|
||||
FakePitchedSoundStream::FakePitchedSoundStream(
|
||||
std::shared_ptr<sf::SoundBuffer> sample_,
|
||||
float pitch_
|
||||
) :
|
||||
pitch(pitch_),
|
||||
sample(sample_)
|
||||
{
|
||||
finish_initializing_the_sample();
|
||||
}
|
||||
|
||||
void FakePitchedSoundStream::finish_initializing_the_sample() {
|
||||
sf::SoundStream::initialize(sample->getChannelCount(), sample->getSampleRate());
|
||||
output_buffer.resize(openAL_time_to_samples(sf::seconds(1)), 0);
|
||||
}
|
||||
|
||||
std::int64_t FakePitchedSoundStream::openAL_time_to_samples(sf::Time position) const {
|
||||
// Always ROUND, no unchecked truncation, hence the addition in the numerator.
|
||||
// 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<std::int64_t>(position.asMicroseconds()) * sample->getSampleRate() * sample->getChannelCount()) + 500000) / 1000000;
|
||||
}
|
||||
|
||||
sf::Time FakePitchedSoundStream::samples_to_openAL_time(std::int64_t samples) const {
|
||||
sf::Time position = sf::Time::Zero;
|
||||
|
||||
// Make sure we don't divide by 0
|
||||
if (sample->getSampleRate() != 0 && sample->getChannelCount() != 0)
|
||||
position = sf::microseconds((samples * 1000000) / (sample->getChannelCount() * sample->getSampleRate()));
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
std::int64_t FakePitchedSoundStream::music_time_to_samples(sf::Time position) const {
|
||||
return openAL_time_to_samples(position / pitch);
|
||||
}
|
||||
|
||||
sf::Time FakePitchedSoundStream::samples_to_music_time(std::int64_t samples) const {
|
||||
return samples_to_openAL_time(samples) * pitch;
|
||||
}
|
38
src/custom_sfml_audio/fake_pitched_sound_stream.hpp
Normal file
38
src/custom_sfml_audio/fake_pitched_sound_stream.hpp
Normal file
@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <SFML/Audio/SoundBuffer.hpp>
|
||||
|
||||
#include "precise_sound_stream.hpp"
|
||||
|
||||
/*
|
||||
SoundStream that doesn't pitch-shift but differenciates between the current
|
||||
offset in the music file played alongside (the music time) and the current
|
||||
offset it reports to openAL (the OpenAL time)
|
||||
|
||||
This allows note claps and beat ticks not to get pitch-shifted when the music
|
||||
is.
|
||||
*/
|
||||
class FakePitchedSoundStream : public PreciseSoundStream {
|
||||
public:
|
||||
FakePitchedSoundStream(
|
||||
const std::filesystem::path& path_to_sample,
|
||||
float pitch_
|
||||
);
|
||||
|
||||
FakePitchedSoundStream(
|
||||
std::shared_ptr<sf::SoundBuffer> sample_,
|
||||
float pitch_
|
||||
);
|
||||
protected:
|
||||
void finish_initializing_the_sample();
|
||||
|
||||
float pitch = 1.f;
|
||||
std::shared_ptr<sf::SoundBuffer> sample;
|
||||
std::vector<sf::Int16> output_buffer;
|
||||
std::int64_t first_sample_of_next_buffer = 0;
|
||||
|
||||
std::int64_t openAL_time_to_samples(sf::Time position) const;
|
||||
sf::Time samples_to_openAL_time(std::int64_t samples) const;
|
||||
std::int64_t music_time_to_samples(sf::Time position) const;
|
||||
sf::Time samples_to_music_time(std::int64_t samples) const;
|
||||
};
|
@ -3,9 +3,11 @@ sources += files([
|
||||
'al_resource.cpp',
|
||||
'audio_device.cpp',
|
||||
'beat_ticks.cpp',
|
||||
'fake_pitched_sound_stream.cpp',
|
||||
'note_claps.cpp',
|
||||
'open_music.cpp',
|
||||
'open_sound_stream.cpp',
|
||||
'precise_sound_stream.cpp',
|
||||
'sampler_callback.cpp',
|
||||
'synced_sound_streams.cpp'
|
||||
])
|
@ -7,6 +7,8 @@
|
||||
#include <stdexcept>
|
||||
|
||||
#include "../better_note.hpp"
|
||||
#include "src/custom_sfml_audio/fake_pitched_sound_stream.hpp"
|
||||
#include "src/custom_sfml_audio/sampler_callback.hpp"
|
||||
|
||||
NoteClaps::NoteClaps(
|
||||
const better::Notes* notes_,
|
||||
@ -14,32 +16,21 @@ NoteClaps::NoteClaps(
|
||||
const std::filesystem::path& assets,
|
||||
float pitch_
|
||||
) :
|
||||
pitch(pitch_),
|
||||
FakePitchedSoundStream(assets / "sounds" / "note.wav", pitch_),
|
||||
notes(notes_),
|
||||
timing(timing_),
|
||||
note_clap(std::make_shared<sf::SoundBuffer>())
|
||||
{
|
||||
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(openAL_time_to_samples(sf::seconds(1)), 0);
|
||||
}
|
||||
timing(timing_)
|
||||
{}
|
||||
|
||||
NoteClaps::NoteClaps(
|
||||
const better::Notes* notes_,
|
||||
const better::Timing* timing_,
|
||||
std::shared_ptr<sf::SoundBuffer> note_clap_,
|
||||
float pitch_
|
||||
std::shared_ptr<sf::SoundBuffer> note_clap,
|
||||
float pitch
|
||||
) :
|
||||
pitch(pitch_),
|
||||
FakePitchedSoundStream(note_clap, pitch),
|
||||
notes(notes_),
|
||||
timing(timing_),
|
||||
note_clap(note_clap_)
|
||||
{
|
||||
sf::SoundStream::initialize(note_clap->getChannelCount(), note_clap->getSampleRate());
|
||||
samples.resize(openAL_time_to_samples(sf::seconds(1)), 0);
|
||||
}
|
||||
timing(timing_)
|
||||
{}
|
||||
|
||||
void NoteClaps::set_notes_and_timing(const better::Notes* notes_, const better::Timing* timing_) {
|
||||
notes = notes_;
|
||||
@ -50,16 +41,15 @@ std::shared_ptr<NoteClaps> NoteClaps::with_pitch(float pitch) {
|
||||
return std::make_shared<NoteClaps>(
|
||||
notes,
|
||||
timing,
|
||||
note_clap,
|
||||
sample,
|
||||
pitch
|
||||
);
|
||||
}
|
||||
|
||||
bool NoteClaps::onGetData(sf::SoundStream::Chunk& data) {
|
||||
samples.assign(samples.size(), 0);
|
||||
if (timing != nullptr and notes != nullptr) {
|
||||
const auto absolute_buffer_start = current_sample;
|
||||
const std::int64_t absolute_buffer_end = current_sample + static_cast<std::int64_t>(samples.size());
|
||||
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);
|
||||
@ -77,90 +67,25 @@ bool NoteClaps::onGetData(sf::SoundStream::Chunk& data) {
|
||||
// don't want claps that *start* at the end sample since
|
||||
// it's an *exculsive* end
|
||||
if (sample < absolute_buffer_end) {
|
||||
notes_at_sample[sample] += 1;
|
||||
notes_at_sample.insert(sample);
|
||||
}
|
||||
});
|
||||
|
||||
for (auto it = notes_at_sample.begin(); it != notes_at_sample.end();) {
|
||||
const auto absolute_clap_start = it->first;
|
||||
const auto absolute_clap_end = absolute_clap_start + static_cast<std::int64_t>(note_clap->getSampleCount());
|
||||
const auto absolute_clap_slice_start = std::max(
|
||||
absolute_clap_start,
|
||||
absolute_buffer_start
|
||||
);
|
||||
const auto absolute_clap_deoverlapped_end = std::min(
|
||||
absolute_clap_end,
|
||||
[&](const auto& it){
|
||||
const auto next = std::next(it);
|
||||
if (next != notes_at_sample.end()) {
|
||||
return next->first;
|
||||
} else {
|
||||
return std::numeric_limits<std::int64_t>::max();
|
||||
}
|
||||
}(it)
|
||||
);
|
||||
if (absolute_clap_deoverlapped_end <= absolute_buffer_start) {
|
||||
it = notes_at_sample.erase(it);
|
||||
continue;
|
||||
}
|
||||
const auto absolute_clap_slice_end = std::min(
|
||||
absolute_clap_deoverlapped_end,
|
||||
absolute_buffer_end
|
||||
);
|
||||
const auto slice_size = absolute_clap_slice_end - absolute_clap_slice_start;
|
||||
const auto slice_start_relative_to_clap_start = absolute_clap_slice_start - absolute_clap_start;
|
||||
const auto slice_start_relative_to_buffer_start = absolute_clap_slice_start - absolute_buffer_start;
|
||||
const auto input_start = note_clap->getSamples() + slice_start_relative_to_clap_start;
|
||||
const auto input_end = input_start + slice_size;
|
||||
const auto output_start = samples.begin() + slice_start_relative_to_buffer_start;
|
||||
std::copy(
|
||||
input_start,
|
||||
input_end,
|
||||
output_start
|
||||
);
|
||||
// has this clap been fully played in this buffer ?
|
||||
if (absolute_clap_deoverlapped_end <= absolute_buffer_end) {
|
||||
it = notes_at_sample.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
copy_sample_at_points(
|
||||
sample,
|
||||
output_buffer,
|
||||
notes_at_sample,
|
||||
absolute_buffer_start
|
||||
);
|
||||
}
|
||||
|
||||
data.samples = samples.data();
|
||||
data.sampleCount = samples.size();
|
||||
current_sample += samples.size();
|
||||
data.samples = output_buffer.data();
|
||||
data.sampleCount = output_buffer.size();
|
||||
first_sample_of_next_buffer += output_buffer.size();
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
void NoteClaps::onSeek(sf::Time timeOffset) {
|
||||
current_sample = music_time_to_samples(timeOffset);
|
||||
first_sample_of_next_buffer = music_time_to_samples(timeOffset);
|
||||
notes_at_sample.clear();
|
||||
};
|
||||
|
||||
std::int64_t NoteClaps::openAL_time_to_samples(sf::Time position) const {
|
||||
// Always ROUND, no unchecked truncation, hence the addition in the numerator.
|
||||
// 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<std::int64_t>(position.asMicroseconds()) * note_clap->getSampleRate() * note_clap->getChannelCount()) + 500000) / 1000000;
|
||||
}
|
||||
|
||||
sf::Time NoteClaps::samples_to_openAL_time(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()));
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
std::int64_t NoteClaps::music_time_to_samples(sf::Time position) const {
|
||||
return openAL_time_to_samples(position / pitch);
|
||||
}
|
||||
|
||||
sf::Time NoteClaps::samples_to_music_time(std::int64_t samples) const {
|
||||
return samples_to_openAL_time(samples) * pitch;
|
||||
}
|
@ -8,9 +8,10 @@
|
||||
|
||||
#include "../better_notes.hpp"
|
||||
#include "../better_timing.hpp"
|
||||
#include "fake_pitched_sound_stream.hpp"
|
||||
#include "precise_sound_stream.hpp"
|
||||
|
||||
class NoteClaps: public PreciseSoundStream {
|
||||
class NoteClaps: public FakePitchedSoundStream {
|
||||
public:
|
||||
NoteClaps(
|
||||
const better::Notes* notes_,
|
||||
@ -35,17 +36,8 @@ protected:
|
||||
void onSeek(sf::Time timeOffset) override;
|
||||
|
||||
private:
|
||||
float pitch = 1.f;
|
||||
std::vector<sf::Int16> samples;
|
||||
std::int64_t current_sample = 0;
|
||||
std::int64_t openAL_time_to_samples(sf::Time position) const;
|
||||
sf::Time samples_to_openAL_time(std::int64_t samples) const;
|
||||
std::int64_t music_time_to_samples(sf::Time position) const;
|
||||
sf::Time samples_to_music_time(std::int64_t samples) const;
|
||||
|
||||
std::map<std::int64_t, unsigned int> notes_at_sample;
|
||||
std::set<std::int64_t> notes_at_sample;
|
||||
|
||||
const better::Notes* notes;
|
||||
const better::Timing* timing;
|
||||
std::shared_ptr<sf::SoundBuffer> note_clap;
|
||||
};
|
62
src/custom_sfml_audio/sampler_callback.cpp
Normal file
62
src/custom_sfml_audio/sampler_callback.cpp
Normal file
@ -0,0 +1,62 @@
|
||||
#include "sampler_callback.hpp"
|
||||
|
||||
void copy_sample_at_points(
|
||||
const std::shared_ptr<sf::SoundBuffer>& sample,
|
||||
std::span<sf::Int16> output_buffer,
|
||||
std::set<std::int64_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;
|
||||
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;
|
||||
} 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 case 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;
|
||||
}
|
||||
}
|
||||
}
|
15
src/custom_sfml_audio/sampler_callback.hpp
Normal file
15
src/custom_sfml_audio/sampler_callback.hpp
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
|
||||
#include <SFML/Audio/SoundBuffer.hpp>
|
||||
#include <SFML/Config.hpp>
|
||||
|
||||
void copy_sample_at_points(
|
||||
const std::shared_ptr<sf::SoundBuffer>& sample,
|
||||
std::span<sf::Int16> output_buffer,
|
||||
std::set<std::int64_t>& starting_points,
|
||||
std::int64_t absolute_buffer_start
|
||||
);
|
Loading…
x
Reference in New Issue
Block a user