Skip to content

Instantly share code, notes, and snippets.

@0x1F9F1
Last active May 7, 2024 14:47
Show Gist options
  • Select an option

  • Save 0x1F9F1/394b4b85dacea5319f6b99a7adb8a944 to your computer and use it in GitHub Desktop.

Select an option

Save 0x1F9F1/394b4b85dacea5319f6b99a7adb8a944 to your computer and use it in GitHub Desktop.
SDL_AudioStream latency control
#define SDL_PROP_AUDIO_STREAM_LATENCY_CONTROL "SDL.audio.stream.latency_control"
#define SDL_AUDIO_LATENCY_CONTROL_SAMPLES 16
typedef struct SDL_AudioLatencyControl
{
float target_latency;
float min_latency;
float max_latency;
int gets[SDL_AUDIO_LATENCY_CONTROL_SAMPLES];
int avails[SDL_AUDIO_LATENCY_CONTROL_SAMPLES];
int samples;
float avg_latency;
float ratio;
} SDL_AudioLatencyControl;
static void SDLCALL SDL_AudioLatencyControlGetCallback(void *userdata, SDL_AudioStream *stream, int additional_amount, int total_amount)
{
SDL_AudioLatencyControl *self = userdata;
int i;
self->gets[self->samples] = (int)(total_amount);
self->avails[self->samples] = SDL_GetAudioStreamQueued(stream);
self->samples = (self->samples + 1) % SDL_AUDIO_LATENCY_CONTROL_SAMPLES;
int max_request = self->gets[0];
int min_queued = self->avails[0];
for (i = 1; i < 8; ++i) {
max_request = SDL_max(max_request, self->gets[i]);
min_queued = SDL_min(min_queued, self->avails[i]);
}
self->avg_latency += (((float)min_queued / (float)max_request) - self->avg_latency) * 0.5f;
float ratio = self->ratio;
// Check if we are above or below the target latency
if (self->avg_latency > self->target_latency) {
if (self->avg_latency > self->max_latency * 2.0f) {
// If we are way above the max latency, just clear the stream.
SDL_ClearAudioStream(stream);
ratio = 1.0f;
} else if (ratio < 1.0f) {
// We've been trying to increase the latency, and now we've reached our target.
ratio = 1.0f;
} else if (self->avg_latency > self->max_latency) {
// Slowly decrease the latency.
ratio = 1.001f;
}
} else {
if (ratio > 1.0f) {
// We've been trying to decrease the latency, and now we've reached our target.
ratio = 1.0f;
} else if (self->avg_latency < self->min_latency) {
// Slowly increase the latency.
ratio = 0.999f;
}
}
SDL_SetAudioStreamFrequencyRatio(stream, ratio);
self->ratio = ratio;
}
static void SDLCALL SDL_CleanupLatencyControl(void *userdata, void *value)
{
SDL_free(value);
}
int SDL_SetAudioStreamLatencyControl(SDL_AudioStream *stream, float min_latency, float max_latency)
{
// TODO: Check errors
SDL_AudioLatencyControl *self = SDL_calloc(1, sizeof(*self));
self->min_latency = min_latency;
self->max_latency = max_latency;
self->target_latency = (min_latency + max_latency) * 0.5f;
self->ratio = 1.0f;
SDL_SetPropertyWithCleanup(SDL_GetAudioStreamProperties(stream), SDL_PROP_AUDIO_STREAM_LATENCY_CONTROL, self, SDL_CleanupLatencyControl, NULL);
SDL_SetAudioStreamGetCallback(stream, SDL_AudioLatencyControlGetCallback, self);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment