mirror of
https://gitlab.com/square-game-liberation-front/F.E.I.S.git
synced 2025-02-28 15:30:32 +01:00
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
This commit is contained in:
parent
ed0b43c375
commit
e9bfb69d36
@ -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_) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "note_claps.hpp"
|
||||
|
||||
#include <SFML/Audio/SoundBuffer.hpp>
|
||||
#include <SFML/System/Time.hpp>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
@ -13,13 +14,26 @@ NoteClaps::NoteClaps(
|
||||
) :
|
||||
notes(notes_),
|
||||
timing(timing_),
|
||||
note_clap()
|
||||
note_clap(std::make_shared<sf::SoundBuffer>())
|
||||
{
|
||||
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<sf::SoundBuffer> 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<std::int64_t>(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<std::int64_t>(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<std::int64_t>(it->first) - static_cast<std::int64_t>(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<std::int64_t>(note_clap.getSampleCount());
|
||||
const auto full_clap_end_in_buffer = full_clap_start_in_buffer + static_cast<std::int64_t>(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<std::int64_t>(note_clap.getSampleCount()) - slice_start_in_clap
|
||||
static_cast<std::int64_t>(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<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 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;
|
||||
}
|
@ -18,8 +18,13 @@ public:
|
||||
const std::filesystem::path& assets
|
||||
);
|
||||
|
||||
NoteClaps(
|
||||
const better::Notes* notes_,
|
||||
const better::Timing* timing_,
|
||||
std::shared_ptr<sf::SoundBuffer> note_clap_
|
||||
);
|
||||
|
||||
void set_notes_and_timing(const better::Notes* notes, const better::Timing* timing);
|
||||
std::atomic<bool> 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<sf::SoundBuffer> note_clap;
|
||||
};
|
@ -16,4 +16,9 @@ struct PreciseSoundStream : public OpenSoundStream {
|
||||
LPALGETSOURCEDVSOFT alGetSourcedvSOFT;
|
||||
std::array<sf::Time, 2> alSecOffsetLatencySoft() const;
|
||||
sf::Time lag = sf::Time::Zero;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
sf::Uint64 timeToSamples(sf::Time position, T sample_rate, T channel_count) {
|
||||
return ((static_cast<sf::Uint64>(position.asMicroseconds()) * sample_rate * channel_count) + 500000) / 1000000;
|
||||
};
|
@ -59,44 +59,38 @@ SyncedSoundStreams::~SyncedSoundStreams() {
|
||||
awaitStreamingThread();
|
||||
}
|
||||
|
||||
|
||||
void SyncedSoundStreams::add_stream(const std::string& name, std::shared_ptr<PreciseSoundStream> s) {
|
||||
void SyncedSoundStreams::change_streams(std::function<void()> 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<PreciseSoundStream> 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<sf::Uint64>(
|
||||
timeOffset.asSeconds()
|
||||
* static_cast<float>(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<ALsizei>(data.sampleCount * sizeof(sf::Int16));
|
||||
alCheck(alBufferData(buffer, stream.buffers.m_format, data.samples, size, static_cast<ALsizei>(stream.buffers.m_sampleRate)));
|
||||
|
||||
|
@ -42,21 +42,11 @@ struct Buffers {
|
||||
struct InternalStream {
|
||||
std::shared_ptr<PreciseSoundStream> stream;
|
||||
Buffers buffers;
|
||||
bool reconstruct_on_pitch_change;
|
||||
|
||||
void clear_queue();
|
||||
};
|
||||
|
||||
struct AddStream {
|
||||
std::string name;
|
||||
std::shared_ptr<PreciseSoundStream> stream;
|
||||
};
|
||||
|
||||
struct RemoveStream {
|
||||
std::string name;
|
||||
};
|
||||
|
||||
using ChangeStreamsCommand = std::variant<AddStream, RemoveStream>;
|
||||
|
||||
class SyncedSoundStreams : public AlResource {
|
||||
public:
|
||||
SyncedSoundStreams();
|
||||
@ -85,6 +75,7 @@ protected:
|
||||
void setProcessingInterval(sf::Time interval);
|
||||
|
||||
private:
|
||||
void change_streams(std::function<void()> callback);
|
||||
void streamData();
|
||||
[[nodiscard]] bool fillAndPushBuffer(InternalStream& stream, unsigned int bufferNum, bool immediateLoop = false);
|
||||
[[nodiscard]] bool fillQueues();
|
||||
|
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user