Mercurial > repos > blastem
comparison render_sdl.c @ 1563:6a62434d6bb1
WIP dynamic rate control
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Fri, 13 Apr 2018 22:25:50 -0700 |
parents | d7b0d0ce8ed1 |
children | 48b08986bf8f |
comparison
equal
deleted
inserted
replaced
1562:d7b0d0ce8ed1 | 1563:6a62434d6bb1 |
---|---|
35 static uint8_t render_gl = 1; | 35 static uint8_t render_gl = 1; |
36 static uint8_t scanlines = 0; | 36 static uint8_t scanlines = 0; |
37 | 37 |
38 static uint32_t last_frame = 0; | 38 static uint32_t last_frame = 0; |
39 | 39 |
40 static int16_t * current_psg = NULL; | |
41 static int16_t * current_ym = NULL; | |
42 | |
43 static uint32_t buffer_samples, sample_rate; | 40 static uint32_t buffer_samples, sample_rate; |
44 static uint32_t missing_count; | 41 static uint32_t missing_count; |
45 | 42 |
46 static SDL_mutex * audio_mutex; | 43 static SDL_mutex * audio_mutex; |
47 static SDL_cond * audio_ready; | 44 static SDL_cond * audio_ready; |
52 int16_t *front; | 49 int16_t *front; |
53 int16_t *back; | 50 int16_t *back; |
54 uint64_t buffer_fraction; | 51 uint64_t buffer_fraction; |
55 uint64_t buffer_inc; | 52 uint64_t buffer_inc; |
56 uint32_t buffer_pos; | 53 uint32_t buffer_pos; |
54 uint32_t read_start; | |
55 uint32_t read_end; | |
57 uint32_t lowpass_alpha; | 56 uint32_t lowpass_alpha; |
57 uint32_t mask; | |
58 int16_t last_left; | 58 int16_t last_left; |
59 int16_t last_right; | 59 int16_t last_right; |
60 uint8_t num_channels; | 60 uint8_t num_channels; |
61 uint8_t front_populated; | 61 uint8_t front_populated; |
62 uint8_t adjusted; | |
62 }; | 63 }; |
63 | 64 |
64 static audio_source *audio_sources[8]; | 65 static audio_source *audio_sources[8]; |
65 static uint8_t num_audio_sources; | 66 static uint8_t num_audio_sources; |
67 static uint8_t sync_to_audio; | |
68 static uint32_t min_buffered; | |
66 | 69 |
67 typedef void (*mix_func)(audio_source *audio, void *vstream, int len); | 70 typedef void (*mix_func)(audio_source *audio, void *vstream, int len); |
68 | 71 |
69 static void mix_s16(audio_source *audio, void *vstream, int len) | 72 static void mix_s16(audio_source *audio, void *vstream, int len) |
70 { | 73 { |
71 int samples = len/(sizeof(int16_t)*2); | 74 int samples = len/(sizeof(int16_t)*2); |
72 int16_t *stream = vstream; | 75 int16_t *stream = vstream; |
73 int16_t *end = stream + 2*samples; | 76 int16_t *end = stream + 2*samples; |
74 int16_t *src = audio->front; | 77 int16_t *src = audio->front; |
78 uint32_t i = audio->read_start; | |
79 uint32_t i_end = audio->read_end; | |
80 int16_t *cur = stream; | |
75 if (audio->num_channels == 1) { | 81 if (audio->num_channels == 1) { |
76 for (int16_t *cur = stream; cur < end;) | 82 while (cur < end && i != i_end) |
77 { | 83 { |
78 *(cur++) += *src; | 84 *(cur++) += src[i]; |
79 *(cur++) += *(src++); | 85 *(cur++) += src[i++]; |
86 i &= audio->mask; | |
80 } | 87 } |
81 } else { | 88 } else { |
82 for (int16_t *cur = stream; cur < end;) | 89 while (cur < end && i != i_end) |
83 { | 90 { |
84 *(cur++) += *(src++); | 91 *(cur++) += src[i++]; |
85 *(cur++) += *(src++); | 92 *(cur++) += src[i++]; |
86 } | 93 i &= audio->mask; |
94 } | |
95 } | |
96 if (cur != end) { | |
97 printf("Underflow of %d samples\n", (int)(end-cur)/2); | |
98 } | |
99 if (!sync_to_audio) { | |
100 audio->read_start = i; | |
87 } | 101 } |
88 } | 102 } |
89 | 103 |
90 static void mix_f32(audio_source *audio, void *vstream, int len) | 104 static void mix_f32(audio_source *audio, void *vstream, int len) |
91 { | 105 { |
92 int samples = len/(sizeof(float)*2); | 106 int samples = len/(sizeof(float)*2); |
93 float *stream = vstream; | 107 float *stream = vstream; |
94 float *end = stream + 2*samples; | 108 float *end = stream + 2*samples; |
95 int16_t *src = audio->front; | 109 int16_t *src = audio->front; |
110 uint32_t i = audio->read_start; | |
111 uint32_t i_end = audio->read_end; | |
112 float *cur = stream; | |
96 if (audio->num_channels == 1) { | 113 if (audio->num_channels == 1) { |
97 for (float *cur = stream; cur < end;) | 114 while (cur < end && i != i_end) |
98 { | 115 { |
99 *(cur++) += ((float)*src) / 0x7FFF; | 116 *(cur++) += ((float)src[i]) / 0x7FFF; |
100 *(cur++) += ((float)*(src++)) / 0x7FFF; | 117 *(cur++) += ((float)src[i++]) / 0x7FFF; |
118 i &= audio->mask; | |
101 } | 119 } |
102 } else { | 120 } else { |
103 for (float *cur = stream; cur < end;) | 121 while(cur < end && i != i_end) |
104 { | 122 { |
105 *(cur++) += ((float)*(src++)) / 0x7FFF; | 123 *(cur++) += ((float)src[i++]) / 0x7FFF; |
106 *(cur++) += ((float)*(src++)) / 0x7FFF; | 124 *(cur++) += ((float)src[i++]) / 0x7FFF; |
107 } | 125 i &= audio->mask; |
126 } | |
127 } | |
128 if (cur != end) { | |
129 printf("Underflow of %d samples\n", (int)(end-cur)/2); | |
130 } | |
131 if (!sync_to_audio) { | |
132 audio->read_start = i; | |
108 } | 133 } |
109 } | 134 } |
110 | 135 |
111 static void mix_null(audio_source *audio, void *vstream, int len) | 136 static void mix_null(audio_source *audio, void *vstream, int len) |
112 { | 137 { |
132 if (!quitting && num_populated < num_audio_sources) { | 157 if (!quitting && num_populated < num_audio_sources) { |
133 SDL_CondWait(audio_ready, audio_mutex); | 158 SDL_CondWait(audio_ready, audio_mutex); |
134 } | 159 } |
135 } while(!quitting && num_populated < num_audio_sources); | 160 } while(!quitting && num_populated < num_audio_sources); |
136 if (!quitting) { | 161 if (!quitting) { |
137 //int16_t *end = stream + 2*samples; | |
138 for (uint8_t i = 0; i < num_audio_sources; i++) | 162 for (uint8_t i = 0; i < num_audio_sources; i++) |
139 { | 163 { |
140 mix(audio_sources[i], byte_stream, len); | 164 mix(audio_sources[i], byte_stream, len); |
141 /*int16_t *src = audio_sources[i]->front; | |
142 if (audio_sources[i]->num_channels == 1) { | |
143 for (int16_t *cur = stream; cur < end;) | |
144 { | |
145 *(cur++) += *src; | |
146 *(cur++) += *(src++); | |
147 } | |
148 } else { | |
149 for (int16_t *cur = stream; cur < end;) | |
150 { | |
151 *(cur++) += *(src++); | |
152 *(cur++) += *(src++); | |
153 } | |
154 }*/ | |
155 audio_sources[i]->front_populated = 0; | 165 audio_sources[i]->front_populated = 0; |
156 SDL_CondSignal(audio_sources[i]->cond); | 166 SDL_CondSignal(audio_sources[i]->cond); |
157 } | 167 } |
158 } | 168 } |
159 SDL_UnlockMutex(audio_mutex); | 169 SDL_UnlockMutex(audio_mutex); |
170 } | |
171 | |
172 static int32_t buffered_diff_accum, accum_count, last_buffered = -1; | |
173 static uint8_t need_adjust; | |
174 static float adjust_ratio; | |
175 #define MIN_ACCUM_COUNT 3 | |
176 #define BUFFER_FRAMES_THRESHOLD 6 | |
177 #define MAX_ADJUST 0.01 | |
178 static void audio_callback_drc(void *userData, uint8_t *byte_stream, int len) | |
179 { | |
180 //TODO: update progress tracking so we can adjust resample rate | |
181 memset(byte_stream, 0, len); | |
182 uint32_t min_buffered = 0xFFFFFFFF; | |
183 uint32_t min_remaining_buffer = 0xFFFFFFFF; | |
184 for (uint8_t i = 0; i < num_audio_sources; i++) | |
185 { | |
186 mix(audio_sources[i], byte_stream, len); | |
187 uint32_t buffered = (audio_sources[i]->read_end - audio_sources[i]->read_start) & audio_sources[i]->mask; | |
188 buffered /= audio_sources[i]->num_channels; | |
189 min_buffered = buffered < min_buffered ? buffered : min_buffered; | |
190 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; | |
192 } | |
193 if (last_buffered > -1) { | |
194 buffered_diff_accum += (int32_t)min_buffered - last_buffered; | |
195 accum_count++; | |
196 } | |
197 last_buffered = min_buffered; | |
198 if (accum_count > MIN_ACCUM_COUNT) { | |
199 float avg_change = (float)buffered_diff_accum / (float)accum_count, frames_to_problem; | |
200 if (buffered_diff_accum < 0) { | |
201 frames_to_problem = (float)min_buffered / -avg_change; | |
202 } else { | |
203 frames_to_problem = (float)min_remaining_buffer / avg_change; | |
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) { | |
212 adjust_ratio = adjust_ratio > 0 ? MAX_ADJUST : -MAX_ADJUST; | |
213 } | |
214 printf("frames_to_problem: %f, avg_change: %f, adjust_ratio: %f\n", frames_to_problem, avg_change, adjust_ratio); | |
215 for (uint8_t i = 0; i < num_audio_sources; i++) | |
216 { | |
217 audio_sources[i]->adjusted = 0; | |
218 } | |
219 } else { | |
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); | |
221 } | |
222 } else { | |
223 printf("accum_count: %d, min_buffered: %d\n", accum_count, min_buffered); | |
224 } | |
225 if (abs(buffered_diff_accum) > 0x10000000) { | |
226 buffered_diff_accum /= 2; | |
227 accum_count /= 2; | |
228 } | |
229 } | |
230 | |
231 static void lock_audio() | |
232 { | |
233 if (sync_to_audio) { | |
234 SDL_LockMutex(audio_mutex); | |
235 } else { | |
236 SDL_LockAudio(); | |
237 } | |
238 } | |
239 | |
240 static void unlock_audio() | |
241 { | |
242 if (sync_to_audio) { | |
243 SDL_UnlockMutex(audio_mutex); | |
244 } else { | |
245 SDL_UnlockAudio(); | |
246 } | |
160 } | 247 } |
161 | 248 |
162 static void render_close_audio() | 249 static void render_close_audio() |
163 { | 250 { |
164 SDL_LockMutex(audio_mutex); | 251 SDL_LockMutex(audio_mutex); |
176 } | 263 } |
177 | 264 |
178 audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider, uint8_t channels) | 265 audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider, uint8_t channels) |
179 { | 266 { |
180 audio_source *ret = NULL; | 267 audio_source *ret = NULL; |
181 SDL_LockMutex(audio_mutex); | 268 uint32_t alloc_size = sync_to_audio ? channels * buffer_samples : nearest_pow2(min_buffered * 2 * channels); |
269 lock_audio(); | |
182 if (num_audio_sources < 8) { | 270 if (num_audio_sources < 8) { |
183 ret = malloc(sizeof(audio_source)); | 271 ret = malloc(sizeof(audio_source)); |
184 ret->front = malloc(channels * buffer_samples * sizeof(int16_t)); | 272 ret->back = malloc(alloc_size * sizeof(int16_t)); |
185 ret->back = malloc(channels * buffer_samples * sizeof(int16_t)); | 273 ret->front = sync_to_audio ? malloc(alloc_size * sizeof(int16_t)) : ret->back; |
186 ret->front_populated = 0; | 274 ret->front_populated = 0; |
187 ret->cond = SDL_CreateCond(); | 275 ret->cond = SDL_CreateCond(); |
188 ret->num_channels = channels; | 276 ret->num_channels = channels; |
277 ret->adjusted = 0; | |
189 audio_sources[num_audio_sources++] = ret; | 278 audio_sources[num_audio_sources++] = ret; |
190 } | 279 } |
191 SDL_UnlockMutex(audio_mutex); | 280 unlock_audio(); |
192 if (!ret) { | 281 if (!ret) { |
193 fatal_error("Too many audio sources!"); | 282 fatal_error("Too many audio sources!"); |
194 } else { | 283 } else { |
195 render_audio_adjust_clock(ret, master_clock, sample_divider); | 284 render_audio_adjust_clock(ret, master_clock, sample_divider); |
196 double lowpass_cutoff = get_lowpass_cutoff(config); | 285 double lowpass_cutoff = get_lowpass_cutoff(config); |
199 double alpha = dt / (dt + rc); | 288 double alpha = dt / (dt + rc); |
200 ret->lowpass_alpha = (int32_t)(((double)0x10000) * alpha); | 289 ret->lowpass_alpha = (int32_t)(((double)0x10000) * alpha); |
201 ret->buffer_pos = 0; | 290 ret->buffer_pos = 0; |
202 ret->buffer_fraction = 0; | 291 ret->buffer_fraction = 0; |
203 ret->last_left = ret->last_right = 0; | 292 ret->last_left = ret->last_right = 0; |
293 ret->read_start = ret->read_end = 0; | |
294 ret->mask = alloc_size-1; | |
204 } | 295 } |
205 return ret; | 296 return ret; |
206 } | 297 } |
207 | 298 |
208 void render_pause_source(audio_source *src) | 299 void render_pause_source(audio_source *src) |
209 { | 300 { |
210 SDL_LockMutex(audio_mutex); | 301 uint8_t need_pause = 0; |
302 lock_audio(); | |
211 for (uint8_t i = 0; i < num_audio_sources; i++) | 303 for (uint8_t i = 0; i < num_audio_sources; i++) |
212 { | 304 { |
213 if (audio_sources[i] == src) { | 305 if (audio_sources[i] == src) { |
214 audio_sources[i] = audio_sources[--num_audio_sources]; | 306 audio_sources[i] = audio_sources[--num_audio_sources]; |
215 SDL_CondSignal(audio_ready); | 307 if (sync_to_audio) { |
308 SDL_CondSignal(audio_ready); | |
309 } | |
216 break; | 310 break; |
217 } | 311 } |
218 } | 312 } |
219 SDL_UnlockMutex(audio_mutex); | 313 if (!num_audio_sources) { |
314 need_pause = 1; | |
315 } | |
316 unlock_audio(); | |
317 if (need_pause) { | |
318 SDL_PauseAudio(1); | |
319 } | |
220 } | 320 } |
221 | 321 |
222 void render_resume_source(audio_source *src) | 322 void render_resume_source(audio_source *src) |
223 { | 323 { |
224 SDL_LockMutex(audio_mutex); | 324 lock_audio(); |
225 if (num_audio_sources < 8) { | 325 if (num_audio_sources < 8) { |
226 audio_sources[num_audio_sources++] = src; | 326 audio_sources[num_audio_sources++] = src; |
227 } | 327 } |
228 SDL_UnlockMutex(audio_mutex); | 328 unlock_audio(); |
329 if (sync_to_audio) { | |
330 SDL_PauseAudio(0); | |
331 } | |
229 } | 332 } |
230 | 333 |
231 void render_free_source(audio_source *src) | 334 void render_free_source(audio_source *src) |
232 { | 335 { |
233 render_pause_source(src); | 336 render_pause_source(src); |
234 | 337 |
235 free(src->front); | 338 free(src->front); |
236 free(src->back); | 339 free(src->back); |
237 SDL_DestroyCond(src->cond); | 340 if (sync_to_audio) { |
341 SDL_DestroyCond(src->cond); | |
342 } | |
238 free(src); | 343 free(src); |
239 } | 344 } |
240 | 345 static uint32_t sync_samples; |
241 static void do_audio_ready(audio_source *src) | 346 static void do_audio_ready(audio_source *src) |
242 { | 347 { |
243 SDL_LockMutex(audio_mutex); | 348 if (sync_to_audio) { |
244 while (src->front_populated) { | 349 SDL_LockMutex(audio_mutex); |
245 SDL_CondWait(src->cond, audio_mutex); | 350 while (src->front_populated) { |
246 } | 351 SDL_CondWait(src->cond, audio_mutex); |
247 int16_t *tmp = src->front; | 352 } |
248 src->front = src->back; | 353 int16_t *tmp = src->front; |
249 src->back = tmp; | 354 src->front = src->back; |
250 src->front_populated = 1; | 355 src->back = tmp; |
251 src->buffer_pos = 0; | 356 src->front_populated = 1; |
252 SDL_CondSignal(audio_ready); | 357 src->buffer_pos = 0; |
253 SDL_UnlockMutex(audio_mutex); | 358 SDL_CondSignal(audio_ready); |
359 SDL_UnlockMutex(audio_mutex); | |
360 } else { | |
361 uint32_t num_buffered; | |
362 SDL_LockAudio(); | |
363 src->read_end = src->buffer_pos; | |
364 num_buffered = (src->read_end - src->read_start) & src->mask; | |
365 if (need_adjust && !src->adjusted) { | |
366 src->adjusted = 1; | |
367 need_adjust--; | |
368 src->buffer_inc = ((double)src->buffer_inc) + ((double)src->buffer_inc) * adjust_ratio + 0.5; | |
369 } | |
370 SDL_UnlockAudio(); | |
371 if (num_buffered >= min_buffered && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) { | |
372 SDL_PauseAudio(0); | |
373 } | |
374 } | |
254 } | 375 } |
255 | 376 |
256 static int16_t lowpass_sample(audio_source *src, int16_t last, int16_t current) | 377 static int16_t lowpass_sample(audio_source *src, int16_t last, int16_t current) |
257 { | 378 { |
258 int32_t tmp = current * src->lowpass_alpha + last * (0x10000 - src->lowpass_alpha); | 379 int32_t tmp = current * src->lowpass_alpha + last * (0x10000 - src->lowpass_alpha); |
274 while (src->buffer_fraction > BUFFER_INC_RES) | 395 while (src->buffer_fraction > BUFFER_INC_RES) |
275 { | 396 { |
276 src->buffer_fraction -= BUFFER_INC_RES; | 397 src->buffer_fraction -= BUFFER_INC_RES; |
277 interp_sample(src, src->last_left, value); | 398 interp_sample(src, src->last_left, value); |
278 | 399 |
279 if (src->buffer_pos == buffer_samples) { | 400 if (((src->buffer_pos - src->read_end) & src->mask) >= sync_samples) { |
280 do_audio_ready(src); | 401 do_audio_ready(src); |
281 } | 402 } |
403 src->buffer_pos &= src->mask; | |
282 } | 404 } |
283 src->last_left = value; | 405 src->last_left = value; |
284 } | 406 } |
285 | 407 |
286 void render_put_stereo_sample(audio_source *src, int16_t left, int16_t right) | 408 void render_put_stereo_sample(audio_source *src, int16_t left, int16_t right) |
293 src->buffer_fraction -= BUFFER_INC_RES; | 415 src->buffer_fraction -= BUFFER_INC_RES; |
294 | 416 |
295 interp_sample(src, src->last_left, left); | 417 interp_sample(src, src->last_left, left); |
296 interp_sample(src, src->last_right, right); | 418 interp_sample(src, src->last_right, right); |
297 | 419 |
298 if (src->buffer_pos == buffer_samples * 2) { | 420 if (((src->buffer_pos - src->read_end) & src->mask)/2 >= sync_samples) { |
299 do_audio_ready(src); | 421 do_audio_ready(src); |
300 } | 422 } |
423 src->buffer_pos &= src->mask; | |
301 } | 424 } |
302 src->last_left = left; | 425 src->last_left = left; |
303 src->last_right = right; | 426 src->last_right = right; |
304 } | 427 } |
305 | 428 |
534 static uint32_t overscan_bot[NUM_VID_STD] = {1, 17}; | 657 static uint32_t overscan_bot[NUM_VID_STD] = {1, 17}; |
535 static uint32_t overscan_left[NUM_VID_STD] = {13, 13}; | 658 static uint32_t overscan_left[NUM_VID_STD] = {13, 13}; |
536 static uint32_t overscan_right[NUM_VID_STD] = {14, 14}; | 659 static uint32_t overscan_right[NUM_VID_STD] = {14, 14}; |
537 static vid_std video_standard = VID_NTSC; | 660 static vid_std video_standard = VID_NTSC; |
538 static char *vid_std_names[NUM_VID_STD] = {"ntsc", "pal"}; | 661 static char *vid_std_names[NUM_VID_STD] = {"ntsc", "pal"}; |
662 static int display_hz; | |
663 static int source_hz; | |
664 static int source_frame; | |
665 static int source_frame_count; | |
666 static int frame_repeat[60]; | |
539 void render_init(int width, int height, char * title, uint8_t fullscreen) | 667 void render_init(int width, int height, char * title, uint8_t fullscreen) |
540 { | 668 { |
541 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) { | 669 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) { |
542 fatal_error("Unable to init SDL: %s\n", SDL_GetError()); | 670 fatal_error("Unable to init SDL: %s\n", SDL_GetError()); |
543 } | 671 } |
549 printf("width: %d, height: %d\n", width, height); | 677 printf("width: %d, height: %d\n", width, height); |
550 windowed_width = width; | 678 windowed_width = width; |
551 windowed_height = height; | 679 windowed_height = height; |
552 | 680 |
553 uint32_t flags = SDL_WINDOW_RESIZABLE; | 681 uint32_t flags = SDL_WINDOW_RESIZABLE; |
682 | |
683 SDL_DisplayMode mode; | |
684 //TODO: Explicit multiple monitor support | |
685 SDL_GetCurrentDisplayMode(0, &mode); | |
686 display_hz = mode.refresh_rate; | |
554 | 687 |
555 if (fullscreen) { | 688 if (fullscreen) { |
556 flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; | 689 flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; |
557 SDL_DisplayMode mode; | |
558 //TODO: Multiple monitor support | |
559 SDL_GetCurrentDisplayMode(0, &mode); | |
560 //the SDL2 migration guide suggests setting width and height to 0 when using SDL_WINDOW_FULLSCREEN_DESKTOP | 690 //the SDL2 migration guide suggests setting width and height to 0 when using SDL_WINDOW_FULLSCREEN_DESKTOP |
561 //but that doesn't seem to work right when using OpenGL, at least on Linux anyway | 691 //but that doesn't seem to work right when using OpenGL, at least on Linux anyway |
562 width = mode.w; | 692 width = mode.w; |
563 height = mode.h; | 693 height = mode.h; |
564 } | 694 } |
565 main_width = width; | 695 main_width = width; |
566 main_height = height; | 696 main_height = height; |
567 is_fullscreen = fullscreen; | 697 is_fullscreen = fullscreen; |
698 | |
699 tern_val def = {.ptrval = "video"}; | |
700 char *sync_src = tern_find_path_default(config, "system\0sync_source\0", def, TVAL_PTR).ptrval; | |
701 sync_to_audio = !strcmp(sync_src, "audio"); | |
568 | 702 |
569 render_gl = 0; | 703 render_gl = 0; |
570 tern_val def = {.ptrval = "off"}; | 704 char *vsync; |
571 char *vsync = tern_find_path_default(config, "video\0vsync\0", def, TVAL_PTR).ptrval; | 705 if (sync_to_audio) { |
706 def.ptrval = "off"; | |
707 vsync = tern_find_path_default(config, "video\0vsync\0", def, TVAL_PTR).ptrval; | |
708 } else { | |
709 vsync = "on"; | |
710 } | |
572 | 711 |
573 tern_node *video = tern_find_node(config, "video"); | 712 tern_node *video = tern_find_node(config, "video"); |
574 if (video) | 713 if (video) |
575 { | 714 { |
576 for (int i = 0; i < NUM_VID_STD; i++) | 715 for (int i = 0; i < NUM_VID_STD; i++) |
686 if (!samples) { | 825 if (!samples) { |
687 samples = 512; | 826 samples = 512; |
688 } | 827 } |
689 printf("config says: %d\n", samples); | 828 printf("config says: %d\n", samples); |
690 desired.samples = samples*2; | 829 desired.samples = samples*2; |
691 desired.callback = audio_callback; | 830 desired.callback = sync_to_audio ? audio_callback : audio_callback_drc; |
692 desired.userdata = NULL; | 831 desired.userdata = NULL; |
693 | 832 |
694 if (SDL_OpenAudio(&desired, &actual) < 0) { | 833 if (SDL_OpenAudio(&desired, &actual) < 0) { |
695 fatal_error("Unable to open SDL audio: %s\n", SDL_GetError()); | 834 fatal_error("Unable to open SDL audio: %s\n", SDL_GetError()); |
696 } | 835 } |
706 } else { | 845 } else { |
707 printf("unsupported format %X\n", actual.format); | 846 printf("unsupported format %X\n", actual.format); |
708 warning("Unsupported audio sample format: %X\n", actual.format); | 847 warning("Unsupported audio sample format: %X\n", actual.format); |
709 mix = mix_null; | 848 mix = mix_null; |
710 } | 849 } |
711 SDL_PauseAudio(0); | |
712 | 850 |
713 uint32_t db_size; | 851 uint32_t db_size; |
714 char *db_data = read_bundled_file("gamecontrollerdb.txt", &db_size); | 852 char *db_data = read_bundled_file("gamecontrollerdb.txt", &db_size); |
715 if (db_data) { | 853 if (db_data) { |
716 int added = SDL_GameControllerAddMappingsFromRW(SDL_RWFromMem(db_data, db_size), 1); | 854 int added = SDL_GameControllerAddMappingsFromRW(SDL_RWFromMem(db_data, db_size), 1); |
717 free(db_data); | 855 free(db_data); |
718 printf("Added %d game controller mappings from gamecontrollerdb.txt\n", added); | 856 printf("Added %d game controller mappings from gamecontrollerdb.txt\n", added); |
719 } | 857 } |
720 | 858 |
721 SDL_JoystickEventState(SDL_ENABLE); | 859 SDL_JoystickEventState(SDL_ENABLE); |
860 | |
861 render_set_video_standard(VID_NTSC); | |
722 | 862 |
723 atexit(render_quit); | 863 atexit(render_quit); |
724 } | 864 } |
725 | 865 |
726 SDL_Window *render_get_window(void) | 866 SDL_Window *render_get_window(void) |
729 } | 869 } |
730 | 870 |
731 void render_set_video_standard(vid_std std) | 871 void render_set_video_standard(vid_std std) |
732 { | 872 { |
733 video_standard = std; | 873 video_standard = std; |
874 source_hz = std == VID_PAL ? 50 : 60; | |
875 uint32_t max_repeat = 0; | |
876 if (abs(source_hz - display_hz) < 2) { | |
877 memset(frame_repeat, 0, sizeof(int)*display_hz); | |
878 } else { | |
879 int inc = display_hz * 100000 / source_hz; | |
880 int accum = 0; | |
881 int dst_frames = 0; | |
882 for (int src_frame = 0; src_frame < source_hz; src_frame++) | |
883 { | |
884 frame_repeat[src_frame] = -1; | |
885 accum += inc; | |
886 while (accum > 100000) | |
887 { | |
888 accum -= 100000; | |
889 frame_repeat[src_frame]++; | |
890 max_repeat = frame_repeat[src_frame] > max_repeat ? frame_repeat[src_frame] : max_repeat; | |
891 dst_frames++; | |
892 } | |
893 } | |
894 if (dst_frames != display_hz) { | |
895 frame_repeat[source_hz-1] += display_hz - dst_frames; | |
896 } | |
897 } | |
898 source_frame = 0; | |
899 source_frame_count = frame_repeat[0]; | |
900 //sync samples with audio thread approximately every 8 lines | |
901 sync_samples = 8 * sample_rate / (source_hz * (VID_PAL ? 313 : 262)); | |
902 max_repeat++; | |
903 min_buffered = (((float)max_repeat * 1.5 * (float)sample_rate/(float)source_hz) / (float)buffer_samples) + 0.9999; | |
904 min_buffered *= buffer_samples; | |
734 } | 905 } |
735 | 906 |
736 void render_update_caption(char *title) | 907 void render_update_caption(char *title) |
737 { | 908 { |
738 caption = title; | 909 caption = title; |
796 static uint32_t last_width, last_height; | 967 static uint32_t last_width, last_height; |
797 static uint8_t interlaced; | 968 static uint8_t interlaced; |
798 void render_framebuffer_updated(uint8_t which, int width) | 969 void render_framebuffer_updated(uint8_t which, int width) |
799 { | 970 { |
800 static uint8_t last; | 971 static uint8_t last; |
972 if (!sync_to_audio && which <= FRAMEBUFFER_EVEN && source_frame_count < 0) { | |
973 source_frame++; | |
974 if (source_frame >= source_hz) { | |
975 source_frame = 0; | |
976 } | |
977 source_frame_count = frame_repeat[source_frame]; | |
978 //TODO: Figure out what to do about SDL Render API texture locking | |
979 return; | |
980 } | |
981 | |
801 last_width = width; | 982 last_width = width; |
802 uint32_t height = which <= FRAMEBUFFER_EVEN | 983 uint32_t height = which <= FRAMEBUFFER_EVEN |
803 ? (video_standard == VID_NTSC ? 243 : 294) - (overscan_top[video_standard] + overscan_bot[video_standard]) | 984 ? (video_standard == VID_NTSC ? 243 : 294) - (overscan_top[video_standard] + overscan_bot[video_standard]) |
804 : 240; | 985 : 240; |
805 FILE *screenshot_file = NULL; | 986 FILE *screenshot_file = NULL; |
906 } | 1087 } |
907 start = last_frame; | 1088 start = last_frame; |
908 frame_counter = 0; | 1089 frame_counter = 0; |
909 } | 1090 } |
910 } | 1091 } |
1092 while (source_frame_count > 0) | |
1093 { | |
1094 render_update_display(); | |
1095 source_frame_count--; | |
1096 } | |
1097 source_frame++; | |
1098 if (source_frame >= source_hz) { | |
1099 source_frame = 0; | |
1100 } | |
1101 source_frame_count = frame_repeat[source_frame]; | |
911 } | 1102 } |
912 | 1103 |
913 static ui_render_fun render_ui; | 1104 static ui_render_fun render_ui; |
914 void render_set_ui_render_fun(ui_render_fun fun) | 1105 void render_set_ui_render_fun(ui_render_fun fun) |
915 { | 1106 { |