From cf561e78a81810fcb70975c7b785938af37b4b64 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 30 Oct 2025 19:56:07 +0300 Subject: [PATCH] fix: stream stops and dies on underflow --- res/scripts/hud.lua | 9 +------- src/audio/AL/ALAudio.cpp | 46 ++++++++++++++++++++++++++-------------- src/audio/AL/ALAudio.hpp | 9 +++++++- src/audio/NoAudio.hpp | 7 ++++++ src/audio/audio.cpp | 11 ++++++++-- src/audio/audio.hpp | 5 +++++ 6 files changed, 60 insertions(+), 27 deletions(-) diff --git a/res/scripts/hud.lua b/res/scripts/hud.lua index ba828491..7730ba5f 100644 --- a/res/scripts/hud.lua +++ b/res/scripts/hud.lua @@ -121,14 +121,7 @@ function on_hud_open() stream = PCMStream(44100, 1, 16) stream:share("test-stream") - local bytes = Bytearray(44100 / 8) - for i=1,#bytes do - local x = math.sin(i * 0.08) * 1 + 0 - bytes[i] = x - end - stream:feed(bytes) - - audio.play_stream_2d("test-stream", 2.0, 1.0, "ui") + streamid = audio.play_stream_2d("test-stream", 2.0, 1.0, "ui") end function on_hud_render() diff --git a/src/audio/AL/ALAudio.cpp b/src/audio/AL/ALAudio.cpp index 267b20dd..83d5c4f4 100644 --- a/src/audio/AL/ALAudio.cpp +++ b/src/audio/AL/ALAudio.cpp @@ -164,9 +164,10 @@ std::unique_ptr ALStream::createSpeaker(bool loop, int channel) { for (uint i = 0; i < ALStream::STREAM_BUFFERS; i++) { uint free_buffer = al->getFreeBuffer(); if (!preloadBuffer(free_buffer, loop)) { - break; + unusedBuffers.push(free_buffer); + } else { + AL_CHECK(alSourceQueueBuffers(free_source, 1, &free_buffer)); } - AL_CHECK(alSourceQueueBuffers(free_source, 1, &free_buffer)); } return std::make_unique(al, free_source, PRIORITY_HIGH, channel); } @@ -213,11 +214,11 @@ void ALStream::unqueueBuffers(uint alsource) { uint ALStream::enqueueBuffers(uint alsource) { uint preloaded = 0; if (!unusedBuffers.empty()) { - uint first_buffer = unusedBuffers.front(); - if (preloadBuffer(first_buffer, loop)) { + uint firstBuffer = unusedBuffers.front(); + if (preloadBuffer(firstBuffer, loop)) { preloaded++; unusedBuffers.pop(); - AL_CHECK(alSourceQueueBuffers(alsource, 1, &first_buffer)); + AL_CHECK(alSourceQueueBuffers(alsource, 1, &firstBuffer)); } } return preloaded; @@ -227,14 +228,14 @@ void ALStream::update(double delta) { if (this->speaker == 0) { return; } - auto p_speaker = audio::get_speaker(this->speaker); - if (p_speaker == nullptr) { + auto speaker = audio::get_speaker(this->speaker); + if (speaker == nullptr) { this->speaker = 0; return; } - ALSpeaker* alspeaker = dynamic_cast(p_speaker); + ALSpeaker* alspeaker = dynamic_cast(speaker); assert(alspeaker != nullptr); - if (alspeaker->stopped) { + if (alspeaker->manuallyStopped) { this->speaker = 0; return; } @@ -245,11 +246,11 @@ void ALStream::update(double delta) { uint preloaded = enqueueBuffers(alsource); // alspeaker->stopped is assigned to false at ALSpeaker::play(...) - if (p_speaker->isStopped() && !alspeaker->stopped) { //TODO: -V560 false-positive? - if (preloaded || dynamic_cast(source.get())) { - p_speaker->play(); - } else { - p_speaker->stop(); + if (speaker->isStopped() && !alspeaker->manuallyStopped) { //TODO: -V560 false-positive? + if (preloaded) { + speaker->play(); + } else if (isStopOnEnd()){ + speaker->stop(); } } } @@ -290,6 +291,14 @@ void ALStream::setTime(duration_t time) { } } +bool ALStream::isStopOnEnd() const { + return stopOnEnd; +} + +void ALStream::setStopOnEnd(bool flag) { + stopOnEnd = flag; +} + ALSpeaker::ALSpeaker(ALAudio* al, uint source, int priority, int channel) : al(al), priority(priority), channel(channel), source(source) { } @@ -356,7 +365,7 @@ void ALSpeaker::setLoop(bool loop) { void ALSpeaker::play() { paused = false; - stopped = false; + manuallyStopped = false; auto p_channel = get_channel(this->channel); AL_CHECK(alSourcef( source, @@ -372,7 +381,7 @@ void ALSpeaker::pause() { } void ALSpeaker::stop() { - stopped = true; + manuallyStopped = true; if (source) { AL_CHECK(alSourceStop(source)); @@ -436,6 +445,11 @@ int ALSpeaker::getPriority() const { return priority; } + +bool ALSpeaker::isManuallyStopped() const { + return manuallyStopped; +} + static bool alc_enumeration_ext = false; ALAudio::ALAudio(ALCdevice* device, ALCcontext* context) diff --git a/src/audio/AL/ALAudio.hpp b/src/audio/AL/ALAudio.hpp index 6fef62f0..39e6f337 100644 --- a/src/audio/AL/ALAudio.hpp +++ b/src/audio/AL/ALAudio.hpp @@ -58,6 +58,7 @@ namespace audio { bool keepSource; char buffer[BUFFER_SIZE]; bool loop = false; + bool stopOnEnd = false; bool preloadBuffer(uint buffer, bool loop); void unqueueBuffers(uint alsource); @@ -80,6 +81,10 @@ namespace audio { void setTime(duration_t time) override; static inline constexpr uint STREAM_BUFFERS = 3; + + bool isStopOnEnd() const override; + + void setStopOnEnd(bool stopOnEnd) override; }; class ALInputDevice : public InputDevice { @@ -117,7 +122,7 @@ namespace audio { float volume = 0.0f; public: ALStream* stream = nullptr; - bool stopped = true; + bool manuallyStopped = true; bool paused = false; uint source; duration_t duration = 0.0f; @@ -157,6 +162,8 @@ namespace audio { bool isRelative() const override; int getPriority() const override; + + bool isManuallyStopped() const override; }; class ALAudio : public Backend { diff --git a/src/audio/NoAudio.hpp b/src/audio/NoAudio.hpp index 5012704b..0da1b4d6 100644 --- a/src/audio/NoAudio.hpp +++ b/src/audio/NoAudio.hpp @@ -61,6 +61,13 @@ namespace audio { void setTime(duration_t time) override { } + + bool isStopOnEnd() const override { + return false; + } + + void setStopOnEnd(bool stopOnEnd) override { + } }; class NoAudio : public Backend { diff --git a/src/audio/audio.cpp b/src/audio/audio.cpp index 60f44b04..0069ff12 100644 --- a/src/audio/audio.cpp +++ b/src/audio/audio.cpp @@ -464,8 +464,15 @@ void audio::update(double delta) { speaker->update(channel); } if (speaker->isStopped()) { - streams.erase(it->first); - it = speakers.erase(it); + auto foundStream = streams.find(it->first); + if (foundStream == streams.end() || + (!speaker->isManuallyStopped() && + foundStream->second->isStopOnEnd())) { + streams.erase(it->first); + it = speakers.erase(it); + } else { + it++; + } } else { it++; } diff --git a/src/audio/audio.hpp b/src/audio/audio.hpp index c394398f..551fd7ec 100644 --- a/src/audio/audio.hpp +++ b/src/audio/audio.hpp @@ -221,6 +221,9 @@ namespace audio { /// @brief Set playhead to the selected time /// @param time selected time virtual void setTime(duration_t time) = 0; + + virtual bool isStopOnEnd() const = 0; + virtual void setStopOnEnd(bool stopOnEnd) = 0; }; /// @brief Sound is an audio asset that supposed to support many @@ -355,6 +358,8 @@ namespace audio { inline bool isStopped() const { return getState() == State::stopped; } + + virtual bool isManuallyStopped() const = 0; }; class Backend {