AudioServer: Fix resampling math

This caused resampled audio to freeze the whole audio system after few
minutes of playing (like doom)
This commit is contained in:
Bananymous 2025-12-18 14:59:55 +02:00
parent e2ccc3026f
commit c64159d5c3
2 changed files with 40 additions and 31 deletions

View File

@ -48,7 +48,8 @@ bool AudioServer::on_client_packet(int fd, long smo_key)
} }
audio_buffer.buffer = static_cast<LibAudio::AudioBuffer*>(smo_map(smo_key)); audio_buffer.buffer = static_cast<LibAudio::AudioBuffer*>(smo_map(smo_key));
audio_buffer.sample_frames_queued = 0; audio_buffer.queued_head = audio_buffer.buffer->tail;
if (audio_buffer.buffer == nullptr) if (audio_buffer.buffer == nullptr)
{ {
dwarnln("Failed to map audio buffer: {}", strerror(errno)); dwarnln("Failed to map audio buffer: {}", strerror(errno));
@ -75,6 +76,8 @@ uint64_t AudioServer::update()
const uint32_t samples_played = m_samples_sent - kernel_samples; const uint32_t samples_played = m_samples_sent - kernel_samples;
ASSERT(samples_played % m_channels == 0); ASSERT(samples_played % m_channels == 0);
const uint32_t sample_frames_played = samples_played / m_channels;
for (uint32_t i = 0; i < samples_played; i++) for (uint32_t i = 0; i < samples_played; i++)
m_samples.pop(); m_samples.pop();
m_samples_sent -= samples_played; m_samples_sent -= samples_played;
@ -92,25 +95,21 @@ uint64_t AudioServer::update()
if (buffer.buffer == nullptr) if (buffer.buffer == nullptr)
continue; continue;
const sample_t sample_ratio = buffer.buffer->sample_rate / static_cast<sample_t>(m_sample_rate); if (const size_t sample_frames_queued = buffer.sample_frames_queued())
if (buffer.sample_frames_queued)
{ {
const sample_t sample_ratio = buffer.buffer->sample_rate / static_cast<sample_t>(m_sample_rate);
const uint32_t buffer_sample_frames_played = BAN::Math::min<size_t>( const uint32_t buffer_sample_frames_played = BAN::Math::min<size_t>(
samples_played * sample_ratio / m_channels, sample_frames_played * sample_ratio,
buffer.sample_frames_queued sample_frames_queued
); );
buffer.buffer->tail = (buffer.buffer->tail + buffer_sample_frames_played * buffer.buffer->channels) % buffer.buffer->capacity; buffer.buffer->tail = (buffer.buffer->tail + buffer_sample_frames_played * buffer.buffer->channels) % buffer.buffer->capacity;
buffer.sample_frames_queued -= buffer_sample_frames_played;
} }
if (buffer.buffer->paused) if (buffer.buffer->paused)
continue; continue;
anyone_playing = true; anyone_playing = true;
const uint32_t buffer_total_sample_frames = ((buffer.buffer->capacity + buffer.buffer->head - buffer.buffer->tail) % buffer.buffer->capacity) / buffer.buffer->channels; max_sample_frames_to_queue = BAN::Math::min<size_t>(max_sample_frames_to_queue, buffer.sample_frames_available());
const uint32_t buffer_sample_frames_available = (buffer_total_sample_frames - buffer.sample_frames_queued) / sample_ratio;
max_sample_frames_to_queue = BAN::Math::min<size_t>(max_sample_frames_to_queue, buffer_sample_frames_available);
} }
if (!anyone_playing) if (!anyone_playing)
@ -132,13 +131,8 @@ uint64_t AudioServer::update()
const sample_t sample_ratio = buffer.buffer->sample_rate / static_cast<sample_t>(m_sample_rate); const sample_t sample_ratio = buffer.buffer->sample_rate / static_cast<sample_t>(m_sample_rate);
const uint32_t buffer_total_sample_frames = ((buffer.buffer->capacity + buffer.buffer->head - buffer.buffer->tail) % buffer.buffer->capacity) / buffer.buffer->channels; const size_t sample_frames_to_queue = BAN::Math::min<size_t>(max_sample_frames_to_queue, buffer.sample_frames_available() / sample_ratio);
const uint32_t buffer_sample_frames_available = (buffer_total_sample_frames - buffer.sample_frames_queued) / sample_ratio; if (sample_frames_to_queue == 0)
if (buffer_sample_frames_available == 0)
continue;
const size_t sample_frames_to_queue = BAN::Math::min<size_t>(max_sample_frames_to_queue, buffer_sample_frames_available);
if (sample_frames_to_queue < sample_frames_per_10ms)
continue; continue;
while (m_samples.size() < queued_samples_end + sample_frames_to_queue * m_channels) while (m_samples.size() < queued_samples_end + sample_frames_to_queue * m_channels)
@ -146,7 +140,7 @@ uint64_t AudioServer::update()
const size_t min_channels = BAN::Math::min(m_channels, buffer.buffer->channels); const size_t min_channels = BAN::Math::min(m_channels, buffer.buffer->channels);
const size_t buffer_tail = buffer.buffer->tail + buffer.sample_frames_queued * buffer.buffer->channels; const size_t buffer_tail = buffer.queued_head;
for (size_t i = 0; i < sample_frames_to_queue; i++) for (size_t i = 0; i < sample_frames_to_queue; i++)
{ {
const size_t buffer_frame = i * sample_ratio; const size_t buffer_frame = i * sample_ratio;
@ -154,10 +148,8 @@ uint64_t AudioServer::update()
m_samples[queued_samples_end + i * m_channels + j] += buffer.buffer->samples[(buffer_tail + buffer_frame * buffer.buffer->channels + j) % buffer.buffer->capacity]; m_samples[queued_samples_end + i * m_channels + j] += buffer.buffer->samples[(buffer_tail + buffer_frame * buffer.buffer->channels + j) % buffer.buffer->capacity];
} }
buffer.sample_frames_queued += BAN::Math::min<uint32_t>( const uint32_t buffer_sample_frames = sample_frames_to_queue * sample_ratio;
buffer_total_sample_frames, buffer.queued_head = (buffer_tail + buffer_sample_frames * buffer.buffer->channels) % buffer.buffer->capacity;
BAN::Math::ceil(sample_frames_to_queue * sample_ratio)
);
} }
send_samples(); send_samples();
@ -179,19 +171,26 @@ void AudioServer::reset_kernel_buffer()
const uint32_t samples_played = m_samples_sent - kernel_samples; const uint32_t samples_played = m_samples_sent - kernel_samples;
ASSERT(samples_played % m_channels == 0); ASSERT(samples_played % m_channels == 0);
const uint32_t sample_frames_played = samples_played / m_channels;
m_samples_sent = 0; m_samples_sent = 0;
m_samples.clear(); m_samples.clear();
for (auto& [_, buffer] : m_audio_buffers) for (auto& [_, buffer] : m_audio_buffers)
{ {
if (buffer.buffer == nullptr || buffer.sample_frames_queued == 0) if (buffer.buffer == nullptr)
continue; continue;
if (const size_t sample_frames_queued = buffer.sample_frames_queued())
{
const sample_t sample_ratio = buffer.buffer->sample_rate / static_cast<sample_t>(m_sample_rate);
const uint32_t buffer_sample_frames_played = BAN::Math::min<size_t>( const uint32_t buffer_sample_frames_played = BAN::Math::min<size_t>(
samples_played / m_channels, sample_frames_played * sample_ratio,
buffer.sample_frames_queued sample_frames_queued
); );
buffer.buffer->tail = (buffer.buffer->tail + buffer_sample_frames_played * buffer.buffer->channels) % buffer.buffer->capacity; buffer.buffer->tail = (buffer.buffer->tail + buffer_sample_frames_played * buffer.buffer->channels) % buffer.buffer->capacity;
buffer.sample_frames_queued = 0; buffer.queued_head = buffer.buffer->tail;
}
} }
update(); update();
@ -215,7 +214,7 @@ void AudioServer::send_samples()
auto sample_buffer = buffer.as_span<kernel_sample_t>(); auto sample_buffer = buffer.as_span<kernel_sample_t>();
for (size_t i = 0; i < samples_to_send; i++) for (size_t i = 0; i < samples_to_send; i++)
{ {
sample_buffer[i] = BAN::Math::clamp<double>( sample_buffer[i] = BAN::Math::clamp<sample_t>(
m_samples[m_samples_sent + i] * BAN::numeric_limits<kernel_sample_t>::max(), m_samples[m_samples_sent + i] * BAN::numeric_limits<kernel_sample_t>::max(),
BAN::numeric_limits<kernel_sample_t>::min(), BAN::numeric_limits<kernel_sample_t>::min(),
BAN::numeric_limits<kernel_sample_t>::max() BAN::numeric_limits<kernel_sample_t>::max()

View File

@ -25,7 +25,17 @@ private:
struct ClientInfo struct ClientInfo
{ {
LibAudio::AudioBuffer* buffer; LibAudio::AudioBuffer* buffer;
size_t sample_frames_queued { 0 }; size_t queued_head { 0 };
size_t sample_frames_queued() const
{
return ((buffer->capacity + queued_head - buffer->tail) % buffer->capacity) / buffer->channels;
}
size_t sample_frames_available() const
{
return ((buffer->capacity + buffer->head - queued_head) % buffer->capacity) / buffer->channels;
}
}; };
using sample_t = LibAudio::AudioBuffer::sample_t; using sample_t = LibAudio::AudioBuffer::sample_t;