fix: stream stops and dies on underflow

This commit is contained in:
MihailRis 2025-10-30 19:56:07 +03:00
parent ab7bf9c709
commit cf561e78a8
6 changed files with 60 additions and 27 deletions

View File

@ -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()

View File

@ -164,9 +164,10 @@ std::unique_ptr<Speaker> 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<ALSpeaker>(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<ALSpeaker*>(p_speaker);
ALSpeaker* alspeaker = dynamic_cast<ALSpeaker*>(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<MemoryPCMStream*>(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)

View File

@ -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 {

View File

@ -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 {

View File

@ -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++;
}

View File

@ -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 {