Last active
May 7, 2024 14:47
-
-
Save 0x1F9F1/394b4b85dacea5319f6b99a7adb8a944 to your computer and use it in GitHub Desktop.
SDL_AudioStream latency control
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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