Mercurial > repos > blastem
comparison render_sdl.c @ 1564:48b08986bf8f
Mostly working dynamic rate control. Needs some tweaking, especially for PAL
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 14 Apr 2018 00:07:20 -0700 |
parents | 6a62434d6bb1 |
children | 61fafcbc2c38 |
comparison
equal
deleted
inserted
replaced
1563:6a62434d6bb1 | 1564:48b08986bf8f |
---|---|
65 static audio_source *audio_sources[8]; | 65 static audio_source *audio_sources[8]; |
66 static uint8_t num_audio_sources; | 66 static uint8_t num_audio_sources; |
67 static uint8_t sync_to_audio; | 67 static uint8_t sync_to_audio; |
68 static uint32_t min_buffered; | 68 static uint32_t min_buffered; |
69 | 69 |
70 typedef void (*mix_func)(audio_source *audio, void *vstream, int len); | 70 typedef int32_t (*mix_func)(audio_source *audio, void *vstream, int len); |
71 | 71 |
72 static void mix_s16(audio_source *audio, void *vstream, int len) | 72 static int32_t mix_s16(audio_source *audio, void *vstream, int len) |
73 { | 73 { |
74 int samples = len/(sizeof(int16_t)*2); | 74 int samples = len/(sizeof(int16_t)*2); |
75 int16_t *stream = vstream; | 75 int16_t *stream = vstream; |
76 int16_t *end = stream + 2*samples; | 76 int16_t *end = stream + 2*samples; |
77 int16_t *src = audio->front; | 77 int16_t *src = audio->front; |
91 *(cur++) += src[i++]; | 91 *(cur++) += src[i++]; |
92 *(cur++) += src[i++]; | 92 *(cur++) += src[i++]; |
93 i &= audio->mask; | 93 i &= audio->mask; |
94 } | 94 } |
95 } | 95 } |
96 | |
97 if (!sync_to_audio) { | |
98 audio->read_start = i; | |
99 } | |
96 if (cur != end) { | 100 if (cur != end) { |
97 printf("Underflow of %d samples\n", (int)(end-cur)/2); | 101 printf("Underflow of %d samples\n", (int)(end-cur)/2); |
98 } | 102 return (cur-end)/2; |
99 if (!sync_to_audio) { | 103 } else { |
100 audio->read_start = i; | 104 return ((i_end - i) & audio->mask) / audio->num_channels; |
101 } | 105 } |
102 } | 106 } |
103 | 107 |
104 static void mix_f32(audio_source *audio, void *vstream, int len) | 108 static int32_t mix_f32(audio_source *audio, void *vstream, int len) |
105 { | 109 { |
106 int samples = len/(sizeof(float)*2); | 110 int samples = len/(sizeof(float)*2); |
107 float *stream = vstream; | 111 float *stream = vstream; |
108 float *end = stream + 2*samples; | 112 float *end = stream + 2*samples; |
109 int16_t *src = audio->front; | 113 int16_t *src = audio->front; |
123 *(cur++) += ((float)src[i++]) / 0x7FFF; | 127 *(cur++) += ((float)src[i++]) / 0x7FFF; |
124 *(cur++) += ((float)src[i++]) / 0x7FFF; | 128 *(cur++) += ((float)src[i++]) / 0x7FFF; |
125 i &= audio->mask; | 129 i &= audio->mask; |
126 } | 130 } |
127 } | 131 } |
132 if (!sync_to_audio) { | |
133 audio->read_start = i; | |
134 } | |
128 if (cur != end) { | 135 if (cur != end) { |
129 printf("Underflow of %d samples\n", (int)(end-cur)/2); | 136 printf("Underflow of %d samples\n", (int)(end-cur)/2); |
130 } | 137 return (cur-end)/2; |
131 if (!sync_to_audio) { | 138 } else { |
132 audio->read_start = i; | 139 return ((i_end - i) & audio->mask) / audio->num_channels; |
133 } | 140 } |
134 } | 141 } |
135 | 142 |
136 static void mix_null(audio_source *audio, void *vstream, int len) | 143 static int32_t mix_null(audio_source *audio, void *vstream, int len) |
137 { | 144 { |
145 return 0; | |
138 } | 146 } |
139 | 147 |
140 static mix_func mix; | 148 static mix_func mix; |
141 | 149 |
142 static void audio_callback(void * userdata, uint8_t *byte_stream, int len) | 150 static void audio_callback(void * userdata, uint8_t *byte_stream, int len) |
167 } | 175 } |
168 } | 176 } |
169 SDL_UnlockMutex(audio_mutex); | 177 SDL_UnlockMutex(audio_mutex); |
170 } | 178 } |
171 | 179 |
172 static int32_t buffered_diff_accum, accum_count, last_buffered = -1; | 180 #define NO_LAST_BUFFERED -2000000000 |
173 static uint8_t need_adjust; | 181 static int32_t last_buffered = NO_LAST_BUFFERED; |
174 static float adjust_ratio; | 182 static uint8_t need_adjust, need_pause; |
175 #define MIN_ACCUM_COUNT 3 | 183 static float adjust_ratio, average_change; |
176 #define BUFFER_FRAMES_THRESHOLD 6 | 184 #define BUFFER_FRAMES_THRESHOLD 6 |
177 #define MAX_ADJUST 0.01 | 185 #define MAX_ADJUST 0.000625 |
178 static void audio_callback_drc(void *userData, uint8_t *byte_stream, int len) | 186 static void audio_callback_drc(void *userData, uint8_t *byte_stream, int len) |
179 { | 187 { |
180 //TODO: update progress tracking so we can adjust resample rate | 188 //TODO: update progress tracking so we can adjust resample rate |
181 memset(byte_stream, 0, len); | 189 memset(byte_stream, 0, len); |
182 uint32_t min_buffered = 0xFFFFFFFF; | 190 int32_t min_buffered = 0x7FFFFFFF; |
183 uint32_t min_remaining_buffer = 0xFFFFFFFF; | 191 uint32_t min_remaining_buffer = 0xFFFFFFFF; |
184 for (uint8_t i = 0; i < num_audio_sources; i++) | 192 for (uint8_t i = 0; i < num_audio_sources; i++) |
185 { | 193 { |
186 mix(audio_sources[i], byte_stream, len); | 194 |
187 uint32_t buffered = (audio_sources[i]->read_end - audio_sources[i]->read_start) & audio_sources[i]->mask; | 195 int32_t buffered = mix(audio_sources[i], byte_stream, len); |
188 buffered /= audio_sources[i]->num_channels; | |
189 min_buffered = buffered < min_buffered ? buffered : min_buffered; | 196 min_buffered = buffered < min_buffered ? buffered : min_buffered; |
190 uint32_t remaining = (audio_sources[i]->mask + 1)/audio_sources[i]->num_channels - buffered; | 197 uint32_t remaining = (audio_sources[i]->mask + 1)/audio_sources[i]->num_channels - buffered; |
191 min_remaining_buffer = remaining < min_remaining_buffer ? remaining : min_remaining_buffer; | 198 min_remaining_buffer = remaining < min_remaining_buffer ? remaining : min_remaining_buffer; |
192 } | 199 } |
193 if (last_buffered > -1) { | 200 if (last_buffered > NO_LAST_BUFFERED) { |
194 buffered_diff_accum += (int32_t)min_buffered - last_buffered; | 201 average_change *= 0.8f; |
195 accum_count++; | 202 average_change += ((int32_t)min_buffered - last_buffered) * 0.2f; |
196 } | 203 } |
197 last_buffered = min_buffered; | 204 last_buffered = min_buffered; |
198 if (accum_count > MIN_ACCUM_COUNT) { | 205 float frames_to_problem; |
199 float avg_change = (float)buffered_diff_accum / (float)accum_count, frames_to_problem; | 206 if (average_change < 0) { |
200 if (buffered_diff_accum < 0) { | 207 frames_to_problem = (float)min_buffered / -average_change; |
201 frames_to_problem = (float)min_buffered / -avg_change; | 208 } else { |
209 frames_to_problem = (float)min_remaining_buffer / average_change; | |
210 } | |
211 if (frames_to_problem < BUFFER_FRAMES_THRESHOLD || min_buffered < 0) { | |
212 need_adjust = num_audio_sources; | |
213 if (min_buffered < 0) { | |
214 adjust_ratio = MAX_ADJUST; | |
215 need_pause = 1; | |
202 } else { | 216 } else { |
203 frames_to_problem = (float)min_remaining_buffer / avg_change; | 217 adjust_ratio = -1.5 * average_change / buffer_samples; |
204 } | |
205 if (frames_to_problem < BUFFER_FRAMES_THRESHOLD) { | |
206 need_adjust = num_audio_sources; | |
207 adjust_ratio = 1.5 * avg_change / buffer_samples; | |
208 buffered_diff_accum = 0; | |
209 accum_count = 0; | |
210 last_buffered = -1; | |
211 if (fabsf(adjust_ratio) > MAX_ADJUST) { | 218 if (fabsf(adjust_ratio) > MAX_ADJUST) { |
212 adjust_ratio = adjust_ratio > 0 ? MAX_ADJUST : -MAX_ADJUST; | 219 adjust_ratio = adjust_ratio > 0 ? MAX_ADJUST : -MAX_ADJUST; |
213 } | 220 } |
214 printf("frames_to_problem: %f, avg_change: %f, adjust_ratio: %f\n", frames_to_problem, avg_change, adjust_ratio); | 221 } |
215 for (uint8_t i = 0; i < num_audio_sources; i++) | 222 printf("frames_to_problem: %f, avg_change: %f, adjust_ratio: %f\n", frames_to_problem, average_change, adjust_ratio); |
216 { | 223 average_change = 0; |
217 audio_sources[i]->adjusted = 0; | 224 last_buffered = NO_LAST_BUFFERED; |
218 } | 225 for (uint8_t i = 0; i < num_audio_sources; i++) |
219 } else { | 226 { |
220 printf("no adjust - frames_to_problem: %f, avg_change: %f, min_buffered: %d, min_remaining_buffer: %d\n", frames_to_problem, avg_change, min_buffered, min_remaining_buffer); | 227 audio_sources[i]->adjusted = 0; |
221 } | 228 } |
222 } else { | 229 } else { |
223 printf("accum_count: %d, min_buffered: %d\n", accum_count, min_buffered); | 230 printf("no adjust - frames_to_problem: %f, avg_change: %f, min_buffered: %d, min_remaining_buffer: %d\n", frames_to_problem, average_change, min_buffered, min_remaining_buffer); |
224 } | |
225 if (abs(buffered_diff_accum) > 0x10000000) { | |
226 buffered_diff_accum /= 2; | |
227 accum_count /= 2; | |
228 } | 231 } |
229 } | 232 } |
230 | 233 |
231 static void lock_audio() | 234 static void lock_audio() |
232 { | 235 { |
357 src->buffer_pos = 0; | 360 src->buffer_pos = 0; |
358 SDL_CondSignal(audio_ready); | 361 SDL_CondSignal(audio_ready); |
359 SDL_UnlockMutex(audio_mutex); | 362 SDL_UnlockMutex(audio_mutex); |
360 } else { | 363 } else { |
361 uint32_t num_buffered; | 364 uint32_t num_buffered; |
365 uint8_t local_need_pause; | |
362 SDL_LockAudio(); | 366 SDL_LockAudio(); |
363 src->read_end = src->buffer_pos; | 367 src->read_end = src->buffer_pos; |
364 num_buffered = (src->read_end - src->read_start) & src->mask; | 368 num_buffered = (src->read_end - src->read_start) & src->mask; |
365 if (need_adjust && !src->adjusted) { | 369 if (need_adjust && !src->adjusted) { |
366 src->adjusted = 1; | 370 src->adjusted = 1; |
367 need_adjust--; | 371 need_adjust--; |
368 src->buffer_inc = ((double)src->buffer_inc) + ((double)src->buffer_inc) * adjust_ratio + 0.5; | 372 src->buffer_inc = ((double)src->buffer_inc) + ((double)src->buffer_inc) * adjust_ratio + 0.5; |
369 } | 373 } |
374 local_need_pause = need_pause; | |
375 need_pause = 0; | |
370 SDL_UnlockAudio(); | 376 SDL_UnlockAudio(); |
371 if (num_buffered >= min_buffered && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) { | 377 if (local_need_pause) { |
378 SDL_PauseAudio(1); | |
379 } else if (num_buffered >= min_buffered && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) { | |
372 SDL_PauseAudio(0); | 380 SDL_PauseAudio(0); |
373 } | 381 } |
374 } | 382 } |
375 } | 383 } |
376 | 384 |
898 source_frame = 0; | 906 source_frame = 0; |
899 source_frame_count = frame_repeat[0]; | 907 source_frame_count = frame_repeat[0]; |
900 //sync samples with audio thread approximately every 8 lines | 908 //sync samples with audio thread approximately every 8 lines |
901 sync_samples = 8 * sample_rate / (source_hz * (VID_PAL ? 313 : 262)); | 909 sync_samples = 8 * sample_rate / (source_hz * (VID_PAL ? 313 : 262)); |
902 max_repeat++; | 910 max_repeat++; |
903 min_buffered = (((float)max_repeat * 1.5 * (float)sample_rate/(float)source_hz) / (float)buffer_samples) + 0.9999; | 911 float mult = max_repeat > 1 ? 2.5 : 1.5; |
912 min_buffered = (((float)max_repeat * mult * (float)sample_rate/(float)source_hz) / (float)buffer_samples) + 0.9999; | |
904 min_buffered *= buffer_samples; | 913 min_buffered *= buffer_samples; |
905 } | 914 } |
906 | 915 |
907 void render_update_caption(char *title) | 916 void render_update_caption(char *title) |
908 { | 917 { |