mirror of
https://github.com/minetest/minetest.git
synced 2025-01-06 08:00:24 +01:00
Sounds: Queue more than two buffers if pitch is high (#14515)
Pitch changes playback speed. So always enqueuing 2 buffers did not suffice (and it was unnecessary complicated).
This commit is contained in:
parent
1d673ce075
commit
e12db0c182
@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
|
|
||||||
#include "al_extensions.h"
|
#include "al_extensions.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
#include "sound_constants.h"
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
@ -77,33 +78,27 @@ PlayingSound::PlayingSound(ALuint source_id, std::shared_ptr<ISoundDataOpen> dat
|
|||||||
warn_if_al_error("when creating non-streaming sound");
|
warn_if_al_error("when creating non-streaming sound");
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Start with 2 buffers
|
// Start with first buffer
|
||||||
ALuint buf_ids[2];
|
|
||||||
|
|
||||||
// If m_next_sample_pos >= len_samples (happens only if not looped), one
|
// If m_next_sample_pos >= len_samples (happens only if not looped), buf0
|
||||||
// or both of buf_ids will be 0. Queuing 0 is a NOP.
|
// will be 0. Queuing 0 is a NOP.
|
||||||
|
|
||||||
auto [buf0, buf0_end, offset_in_buf0] = m_data->getOrLoadBufferAt(m_next_sample_pos);
|
auto [buf0, buf0_end, offset_in_buf0] = m_data->getOrLoadBufferAt(m_next_sample_pos);
|
||||||
buf_ids[0] = buf0;
|
|
||||||
m_next_sample_pos = buf0_end;
|
m_next_sample_pos = buf0_end;
|
||||||
|
|
||||||
if (m_looping && m_next_sample_pos == len_samples)
|
alSourceQueueBuffers(m_source_id, 1, &buf0);
|
||||||
m_next_sample_pos = 0;
|
|
||||||
|
|
||||||
auto [buf1, buf1_end, offset_in_buf1] = m_data->getOrLoadBufferAt(m_next_sample_pos);
|
|
||||||
buf_ids[1] = buf1;
|
|
||||||
m_next_sample_pos = buf1_end;
|
|
||||||
assert(offset_in_buf1 == 0);
|
|
||||||
|
|
||||||
alSourceQueueBuffers(m_source_id, 2, buf_ids);
|
|
||||||
alSourcei(m_source_id, AL_SAMPLE_OFFSET, offset_in_buf0);
|
alSourcei(m_source_id, AL_SAMPLE_OFFSET, offset_in_buf0);
|
||||||
|
|
||||||
// We can't use AL_LOOPING because more buffers are queued later
|
// We can't use AL_LOOPING because more buffers are queued later.
|
||||||
// looping is therefore done manually
|
// Looping is therefore done manually.
|
||||||
|
|
||||||
|
// Sound is not dead if queue runs empty prematurely
|
||||||
m_stopped_means_dead = false;
|
m_stopped_means_dead = false;
|
||||||
|
|
||||||
warn_if_al_error("when creating streaming sound");
|
warn_if_al_error("when creating streaming sound");
|
||||||
|
|
||||||
|
// Enqueue more buffers
|
||||||
|
stepStream(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set initial pos, volume, pitch
|
// Set initial pos, volume, pitch
|
||||||
@ -129,23 +124,44 @@ PlayingSound::PlayingSound(ALuint source_id, std::shared_ptr<ISoundDataOpen> dat
|
|||||||
setPitch(pitch);
|
setPitch(pitch);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PlayingSound::stepStream()
|
bool PlayingSound::stepStream(bool playback_speed_changed)
|
||||||
{
|
{
|
||||||
if (isDead())
|
if (isDead())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// unqueue finished buffers
|
// Unqueue finished buffers
|
||||||
ALint num_unqueued_bufs = 0;
|
ALint num_processed_bufs = 0;
|
||||||
alGetSourcei(m_source_id, AL_BUFFERS_PROCESSED, &num_unqueued_bufs);
|
alGetSourcei(m_source_id, AL_BUFFERS_PROCESSED, &num_processed_bufs);
|
||||||
if (num_unqueued_bufs == 0)
|
if (num_processed_bufs == 0 && !playback_speed_changed)
|
||||||
return true;
|
return true; // Nothing to do
|
||||||
// We always have 2 buffers enqueued at most
|
if (num_processed_bufs > 0) {
|
||||||
SANITY_CHECK(num_unqueued_bufs <= 2);
|
ALint num_to_unqueue = num_processed_bufs;
|
||||||
ALuint unqueued_buffer_ids[2];
|
ALuint unqueued_buffer_ids[8];
|
||||||
alSourceUnqueueBuffers(m_source_id, num_unqueued_bufs, unqueued_buffer_ids);
|
while (num_to_unqueue > 8) {
|
||||||
|
alSourceUnqueueBuffers(m_source_id, 8, unqueued_buffer_ids);
|
||||||
|
num_to_unqueue -= 8;
|
||||||
|
}
|
||||||
|
alSourceUnqueueBuffers(m_source_id, num_to_unqueue, unqueued_buffer_ids);
|
||||||
|
}
|
||||||
|
|
||||||
// Fill up again
|
// Find out how many buffers we want to enqueue
|
||||||
for (ALint i = 0; i < num_unqueued_bufs; ++i) {
|
f32 pitch = 1.0f;
|
||||||
|
alGetSourcef(m_source_id, AL_PITCH, &pitch);
|
||||||
|
ALint num_queued_bufs = 0;
|
||||||
|
alGetSourcei(m_source_id, AL_BUFFERS_QUEUED, &num_queued_bufs);
|
||||||
|
// Min. length of untouched buffers
|
||||||
|
const f32 playback_left = MIN_STREAM_BUFFER_LENGTH * std::max(0, num_queued_bufs - 1);
|
||||||
|
// Max. time until next stepStream() call, see also [Streaming of sounds] in
|
||||||
|
// sound_constants.h.
|
||||||
|
// Multiplied by pitch because pitch makes playback faster than real time.
|
||||||
|
// (Does not account for doppler effect, if we had that.)
|
||||||
|
// +0.1 seconds to accommodate hickups.
|
||||||
|
const f32 playback_until_next_check = (2.0f * STREAM_BIGSTEP_TIME + 0.1f) * pitch;
|
||||||
|
const f32 playback_to_fill_up = std::max(0.0f, playback_until_next_check - playback_left);
|
||||||
|
const int num_bufs_to_enqueue = std::ceil(playback_to_fill_up / MIN_STREAM_BUFFER_LENGTH);
|
||||||
|
|
||||||
|
// Fill up
|
||||||
|
for (int i = 0; i < num_bufs_to_enqueue; ++i) {
|
||||||
if (m_next_sample_pos == m_data->m_decode_info.length_samples) {
|
if (m_next_sample_pos == m_data->m_decode_info.length_samples) {
|
||||||
// Reached end
|
// Reached end
|
||||||
if (m_looping) {
|
if (m_looping) {
|
||||||
@ -256,4 +272,11 @@ f32 PlayingSound::getGain() noexcept
|
|||||||
return gain;
|
return gain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PlayingSound::setPitch(f32 pitch)
|
||||||
|
{
|
||||||
|
alSourcef(m_source_id, AL_PITCH, pitch);
|
||||||
|
if (isStreaming())
|
||||||
|
stepStream(true);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace sound
|
} // namespace sound
|
||||||
|
@ -63,7 +63,7 @@ public:
|
|||||||
DISABLE_CLASS_COPY(PlayingSound)
|
DISABLE_CLASS_COPY(PlayingSound)
|
||||||
|
|
||||||
// return false means streaming finished
|
// return false means streaming finished
|
||||||
bool stepStream();
|
bool stepStream(bool playback_speed_changed = false);
|
||||||
|
|
||||||
// retruns true if it wasn't fading already
|
// retruns true if it wasn't fading already
|
||||||
bool fade(f32 step, f32 target_gain) noexcept;
|
bool fade(f32 step, f32 target_gain) noexcept;
|
||||||
@ -77,7 +77,7 @@ public:
|
|||||||
|
|
||||||
f32 getGain() noexcept;
|
f32 getGain() noexcept;
|
||||||
|
|
||||||
void setPitch(f32 pitch) noexcept { alSourcef(m_source_id, AL_PITCH, pitch); }
|
void setPitch(f32 pitch);
|
||||||
|
|
||||||
bool isStreaming() const noexcept { return m_data->isStreaming(); }
|
bool isStreaming() const noexcept { return m_data->isStreaming(); }
|
||||||
|
|
||||||
|
@ -89,14 +89,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
* In the worst case, a sound is stepped at the start of one bigstep and in the
|
* In the worst case, a sound is stepped at the start of one bigstep and in the
|
||||||
* end of the next bigstep. So between two stepStream()-calls lie at most
|
* end of the next bigstep. So between two stepStream()-calls lie at most
|
||||||
* 2 * STREAM_BIGSTEP_TIME seconds.
|
* 2 * STREAM_BIGSTEP_TIME seconds.
|
||||||
* As there are always 2 sound buffers enqueued, at least one untouched full buffer
|
* We ensure that there are always enough untouched full buffers left such that
|
||||||
* is still available after the first stepStream().
|
* we do not run into an empty queue in this time period, see stepStream().
|
||||||
* If we take a MIN_STREAM_BUFFER_LENGTH > 2 * STREAM_BIGSTEP_TIME, we can hence
|
|
||||||
* not run into an empty queue.
|
|
||||||
*
|
|
||||||
* The MIN_STREAM_BUFFER_LENGTH needs to be a little bigger because of dtime jitter,
|
|
||||||
* other sounds that may have taken long to stepStream(), and sounds being played
|
|
||||||
* faster due to Doppler effect.
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -115,8 +109,6 @@ constexpr f32 STREAM_BIGSTEP_TIME = 0.3f;
|
|||||||
// step duration for the OpenALSoundManager thread, in seconds
|
// step duration for the OpenALSoundManager thread, in seconds
|
||||||
constexpr f32 SOUNDTHREAD_DTIME = 0.016f;
|
constexpr f32 SOUNDTHREAD_DTIME = 0.016f;
|
||||||
|
|
||||||
static_assert(MIN_STREAM_BUFFER_LENGTH > STREAM_BIGSTEP_TIME * 2.0f,
|
|
||||||
"See [Streaming of sounds].");
|
|
||||||
static_assert(SOUND_DURATION_MAX_SINGLE >= MIN_STREAM_BUFFER_LENGTH * 2.0f,
|
static_assert(SOUND_DURATION_MAX_SINGLE >= MIN_STREAM_BUFFER_LENGTH * 2.0f,
|
||||||
"There's no benefit in streaming if we can't queue more than 2 buffers.");
|
"There's no benefit in streaming if we can't queue more than 2 buffers.");
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user