Mercurial > repos > blastem
comparison render_sdl.c @ 1931:374a5ae694e8 mame_interp
Merge from default
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 18 Apr 2020 11:42:53 -0700 |
parents | 33c0c4579c1f |
children | b387f1c5a1d0 |
comparison
equal
deleted
inserted
replaced
1843:13abdc98379e | 1931:374a5ae694e8 |
---|---|
44 static uint8_t render_gl = 1; | 44 static uint8_t render_gl = 1; |
45 static uint8_t scanlines = 0; | 45 static uint8_t scanlines = 0; |
46 | 46 |
47 static uint32_t last_frame = 0; | 47 static uint32_t last_frame = 0; |
48 | 48 |
49 static uint8_t output_channels; | |
50 static uint32_t buffer_samples, sample_rate; | |
51 static uint32_t missing_count; | |
52 | |
53 static SDL_mutex * audio_mutex; | 49 static SDL_mutex * audio_mutex; |
54 static SDL_cond * audio_ready; | 50 static SDL_cond * audio_ready; |
55 static uint8_t quitting = 0; | 51 static uint8_t quitting = 0; |
56 | 52 |
57 struct audio_source { | |
58 SDL_cond *cond; | |
59 int16_t *front; | |
60 int16_t *back; | |
61 double dt; | |
62 uint64_t buffer_fraction; | |
63 uint64_t buffer_inc; | |
64 float gain_mult; | |
65 uint32_t buffer_pos; | |
66 uint32_t read_start; | |
67 uint32_t read_end; | |
68 uint32_t lowpass_alpha; | |
69 uint32_t mask; | |
70 int16_t last_left; | |
71 int16_t last_right; | |
72 uint8_t num_channels; | |
73 uint8_t front_populated; | |
74 }; | |
75 | |
76 static audio_source *audio_sources[8]; | |
77 static audio_source *inactive_audio_sources[8]; | |
78 static uint8_t num_audio_sources; | |
79 static uint8_t num_inactive_audio_sources; | |
80 static uint8_t sync_to_audio; | 53 static uint8_t sync_to_audio; |
81 static uint32_t min_buffered; | 54 static uint32_t min_buffered; |
82 static float overall_gain_mult, *mix_buf; | 55 |
83 static int sample_size; | 56 uint32_t render_min_buffered(void) |
84 | 57 { |
85 typedef void (*conv_func)(float *samples, void *vstream, int sample_count); | 58 return min_buffered; |
86 | 59 } |
87 static void convert_null(float *samples, void *vstream, int sample_count) | 60 |
88 { | 61 uint8_t render_is_audio_sync(void) |
89 memset(vstream, 0, sample_count * sample_size); | 62 { |
90 } | 63 return sync_to_audio; |
91 | 64 } |
92 static void convert_s16(float *samples, void *vstream, int sample_count) | 65 |
93 { | 66 void render_buffer_consumed(audio_source *src) |
94 int16_t *stream = vstream; | 67 { |
95 for (int16_t *end = stream + sample_count; stream < end; stream++, samples++) | 68 SDL_CondSignal(src->opaque); |
96 { | 69 } |
97 float sample = *samples; | |
98 int16_t out_sample; | |
99 if (sample >= 1.0f) { | |
100 out_sample = 0x7FFF; | |
101 } else if (sample <= -1.0f) { | |
102 out_sample = -0x8000; | |
103 } else { | |
104 out_sample = sample * 0x7FFF; | |
105 } | |
106 *stream = out_sample; | |
107 } | |
108 } | |
109 | |
110 static void clamp_f32(float *samples, void *vstream, int sample_count) | |
111 { | |
112 for (; sample_count > 0; sample_count--, samples++) | |
113 { | |
114 float sample = *samples; | |
115 if (sample > 1.0f) { | |
116 sample = 1.0f; | |
117 } else if (sample < -1.0f) { | |
118 sample = -1.0f; | |
119 } | |
120 *samples = sample; | |
121 } | |
122 } | |
123 | |
124 static int32_t mix_f32(audio_source *audio, float *stream, int samples) | |
125 { | |
126 float *end = stream + samples; | |
127 int16_t *src = audio->front; | |
128 uint32_t i = audio->read_start; | |
129 uint32_t i_end = audio->read_end; | |
130 float *cur = stream; | |
131 float gain_mult = audio->gain_mult * overall_gain_mult; | |
132 size_t first_add = output_channels > 1 ? 1 : 0, second_add = output_channels > 1 ? output_channels - 1 : 1; | |
133 if (audio->num_channels == 1) { | |
134 while (cur < end && i != i_end) | |
135 { | |
136 *cur += gain_mult * ((float)src[i]) / 0x7FFF; | |
137 cur += first_add; | |
138 *cur += gain_mult * ((float)src[i++]) / 0x7FFF; | |
139 cur += second_add; | |
140 i &= audio->mask; | |
141 } | |
142 } else { | |
143 while(cur < end && i != i_end) | |
144 { | |
145 *cur += gain_mult * ((float)src[i++]) / 0x7FFF; | |
146 cur += first_add; | |
147 *cur += gain_mult * ((float)src[i++]) / 0x7FFF; | |
148 cur += second_add; | |
149 i &= audio->mask; | |
150 } | |
151 } | |
152 if (!sync_to_audio) { | |
153 audio->read_start = i; | |
154 } | |
155 if (cur != end) { | |
156 debug_message("Underflow of %d samples, read_start: %d, read_end: %d, mask: %X\n", (int)(end-cur)/2, audio->read_start, audio->read_end, audio->mask); | |
157 return (cur-end)/2; | |
158 } else { | |
159 return ((i_end - i) & audio->mask) / audio->num_channels; | |
160 } | |
161 } | |
162 | |
163 static conv_func convert; | |
164 | 70 |
165 static void audio_callback(void * userdata, uint8_t *byte_stream, int len) | 71 static void audio_callback(void * userdata, uint8_t *byte_stream, int len) |
166 { | 72 { |
167 uint8_t num_populated; | |
168 SDL_LockMutex(audio_mutex); | 73 SDL_LockMutex(audio_mutex); |
74 uint8_t all_ready; | |
169 do { | 75 do { |
170 num_populated = 0; | 76 all_ready = all_sources_ready(); |
171 for (uint8_t i = 0; i < num_audio_sources; i++) | 77 if (!quitting && !all_ready) { |
172 { | |
173 if (audio_sources[i]->front_populated) { | |
174 num_populated++; | |
175 } | |
176 } | |
177 if (!quitting && num_populated < num_audio_sources) { | |
178 fflush(stdout); | |
179 SDL_CondWait(audio_ready, audio_mutex); | 78 SDL_CondWait(audio_ready, audio_mutex); |
180 } | 79 } |
181 } while(!quitting && num_populated < num_audio_sources); | 80 } while(!quitting && !all_ready); |
182 int samples = len / sample_size; | |
183 float *mix_dest = mix_buf ? mix_buf : (float *)byte_stream; | |
184 memset(mix_dest, 0, samples * sizeof(float)); | |
185 if (!quitting) { | 81 if (!quitting) { |
186 for (uint8_t i = 0; i < num_audio_sources; i++) | 82 mix_and_convert(byte_stream, len, NULL); |
187 { | 83 } |
188 mix_f32(audio_sources[i], mix_dest, samples); | |
189 audio_sources[i]->front_populated = 0; | |
190 SDL_CondSignal(audio_sources[i]->cond); | |
191 } | |
192 } | |
193 convert(mix_dest, byte_stream, samples); | |
194 SDL_UnlockMutex(audio_mutex); | 84 SDL_UnlockMutex(audio_mutex); |
195 } | 85 } |
196 | 86 |
197 #define NO_LAST_BUFFERED -2000000000 | 87 #define NO_LAST_BUFFERED -2000000000 |
198 static int32_t last_buffered = NO_LAST_BUFFERED; | 88 static int32_t last_buffered = NO_LAST_BUFFERED; |
206 { | 96 { |
207 if (cur_min_buffered < 0) { | 97 if (cur_min_buffered < 0) { |
208 //underflow last frame, but main thread hasn't gotten a chance to call SDL_PauseAudio yet | 98 //underflow last frame, but main thread hasn't gotten a chance to call SDL_PauseAudio yet |
209 return; | 99 return; |
210 } | 100 } |
211 cur_min_buffered = 0x7FFFFFFF; | 101 cur_min_buffered = mix_and_convert(byte_stream, len, &min_remaining_buffer); |
212 min_remaining_buffer = 0xFFFFFFFF; | 102 } |
213 float *mix_dest = mix_buf ? mix_buf : (float *)byte_stream; | 103 |
214 int samples = len / sample_size; | 104 void render_lock_audio() |
215 memset(mix_dest, 0, samples * sizeof(float)); | |
216 for (uint8_t i = 0; i < num_audio_sources; i++) | |
217 { | |
218 | |
219 int32_t buffered = mix_f32(audio_sources[i], mix_dest, samples); | |
220 cur_min_buffered = buffered < cur_min_buffered ? buffered : cur_min_buffered; | |
221 uint32_t remaining = (audio_sources[i]->mask + 1)/audio_sources[i]->num_channels - buffered; | |
222 min_remaining_buffer = remaining < min_remaining_buffer ? remaining : min_remaining_buffer; | |
223 } | |
224 convert(mix_dest, byte_stream, samples); | |
225 } | |
226 | |
227 static void lock_audio() | |
228 { | 105 { |
229 if (sync_to_audio) { | 106 if (sync_to_audio) { |
230 SDL_LockMutex(audio_mutex); | 107 SDL_LockMutex(audio_mutex); |
231 } else { | 108 } else { |
232 SDL_LockAudio(); | 109 SDL_LockAudio(); |
233 } | 110 } |
234 } | 111 } |
235 | 112 |
236 static void unlock_audio() | 113 void render_unlock_audio() |
237 { | 114 { |
238 if (sync_to_audio) { | 115 if (sync_to_audio) { |
239 SDL_UnlockMutex(audio_mutex); | 116 SDL_UnlockMutex(audio_mutex); |
240 } else { | 117 } else { |
241 SDL_UnlockAudio(); | 118 SDL_UnlockAudio(); |
247 SDL_LockMutex(audio_mutex); | 124 SDL_LockMutex(audio_mutex); |
248 quitting = 1; | 125 quitting = 1; |
249 SDL_CondSignal(audio_ready); | 126 SDL_CondSignal(audio_ready); |
250 SDL_UnlockMutex(audio_mutex); | 127 SDL_UnlockMutex(audio_mutex); |
251 SDL_CloseAudio(); | 128 SDL_CloseAudio(); |
129 /* | |
130 FIXME: move this to render_audio.c | |
252 if (mix_buf) { | 131 if (mix_buf) { |
253 free(mix_buf); | 132 free(mix_buf); |
254 mix_buf = NULL; | 133 mix_buf = NULL; |
255 } | 134 } |
256 } | 135 */ |
257 | 136 } |
258 #define BUFFER_INC_RES 0x40000000UL | 137 |
259 | 138 void *render_new_audio_opaque(void) |
260 void render_audio_adjust_clock(audio_source *src, uint64_t master_clock, uint64_t sample_divider) | 139 { |
261 { | 140 return SDL_CreateCond(); |
262 src->buffer_inc = ((BUFFER_INC_RES * (uint64_t)sample_rate) / master_clock) * sample_divider; | 141 } |
263 } | 142 |
264 | 143 void render_free_audio_opaque(void *opaque) |
265 audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider, uint8_t channels) | 144 { |
266 { | 145 SDL_DestroyCond(opaque); |
267 audio_source *ret = NULL; | 146 } |
268 uint32_t alloc_size = sync_to_audio ? channels * buffer_samples : nearest_pow2(min_buffered * 4 * channels); | 147 |
269 lock_audio(); | 148 void render_audio_created(audio_source *source) |
270 if (num_audio_sources < 8) { | 149 { |
271 ret = malloc(sizeof(audio_source)); | |
272 ret->back = malloc(alloc_size * sizeof(int16_t)); | |
273 ret->front = sync_to_audio ? malloc(alloc_size * sizeof(int16_t)) : ret->back; | |
274 ret->front_populated = 0; | |
275 ret->cond = SDL_CreateCond(); | |
276 ret->num_channels = channels; | |
277 audio_sources[num_audio_sources++] = ret; | |
278 } | |
279 unlock_audio(); | |
280 if (!ret) { | |
281 fatal_error("Too many audio sources!"); | |
282 } else { | |
283 render_audio_adjust_clock(ret, master_clock, sample_divider); | |
284 double lowpass_cutoff = get_lowpass_cutoff(config); | |
285 double rc = (1.0 / lowpass_cutoff) / (2.0 * M_PI); | |
286 ret->dt = 1.0 / ((double)master_clock / (double)(sample_divider)); | |
287 double alpha = ret->dt / (ret->dt + rc); | |
288 ret->lowpass_alpha = (int32_t)(((double)0x10000) * alpha); | |
289 ret->buffer_pos = 0; | |
290 ret->buffer_fraction = 0; | |
291 ret->last_left = ret->last_right = 0; | |
292 ret->read_start = 0; | |
293 ret->read_end = sync_to_audio ? buffer_samples * channels : 0; | |
294 ret->mask = sync_to_audio ? 0xFFFFFFFF : alloc_size-1; | |
295 ret->gain_mult = 1.0f; | |
296 } | |
297 if (sync_to_audio && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) { | 150 if (sync_to_audio && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) { |
298 SDL_PauseAudio(0); | 151 SDL_PauseAudio(0); |
299 } | 152 } |
300 return ret; | 153 } |
301 } | 154 |
302 | 155 void render_source_paused(audio_source *src, uint8_t remaining_sources) |
303 static float db_to_mult(float gain) | 156 { |
304 { | 157 if (sync_to_audio) { |
305 return powf(10.0f, gain/20.0f); | 158 SDL_CondSignal(audio_ready); |
306 } | 159 } |
307 | 160 if (!remaining_sources) { |
308 void render_audio_source_gaindb(audio_source *src, float gain) | 161 SDL_PauseAudio(0); |
309 { | 162 } |
310 src->gain_mult = db_to_mult(gain); | 163 } |
311 } | 164 |
312 | 165 void render_source_resumed(audio_source *src) |
313 void render_pause_source(audio_source *src) | 166 { |
314 { | |
315 uint8_t need_pause = 0; | |
316 lock_audio(); | |
317 for (uint8_t i = 0; i < num_audio_sources; i++) | |
318 { | |
319 if (audio_sources[i] == src) { | |
320 audio_sources[i] = audio_sources[--num_audio_sources]; | |
321 if (sync_to_audio) { | |
322 SDL_CondSignal(audio_ready); | |
323 } | |
324 break; | |
325 } | |
326 } | |
327 if (!num_audio_sources) { | |
328 need_pause = 1; | |
329 } | |
330 unlock_audio(); | |
331 if (need_pause) { | |
332 SDL_PauseAudio(1); | |
333 } | |
334 inactive_audio_sources[num_inactive_audio_sources++] = src; | |
335 } | |
336 | |
337 void render_resume_source(audio_source *src) | |
338 { | |
339 lock_audio(); | |
340 if (num_audio_sources < 8) { | |
341 audio_sources[num_audio_sources++] = src; | |
342 } | |
343 unlock_audio(); | |
344 for (uint8_t i = 0; i < num_inactive_audio_sources; i++) | |
345 { | |
346 if (inactive_audio_sources[i] == src) { | |
347 inactive_audio_sources[i] = inactive_audio_sources[--num_inactive_audio_sources]; | |
348 } | |
349 } | |
350 if (sync_to_audio) { | 167 if (sync_to_audio) { |
351 SDL_PauseAudio(0); | 168 SDL_PauseAudio(0); |
352 } | 169 } |
353 } | 170 } |
354 | 171 |
355 void render_free_source(audio_source *src) | 172 void render_do_audio_ready(audio_source *src) |
356 { | |
357 render_pause_source(src); | |
358 | |
359 free(src->front); | |
360 if (sync_to_audio) { | |
361 free(src->back); | |
362 SDL_DestroyCond(src->cond); | |
363 } | |
364 free(src); | |
365 } | |
366 static uint32_t sync_samples; | |
367 static void do_audio_ready(audio_source *src) | |
368 { | 173 { |
369 if (sync_to_audio) { | 174 if (sync_to_audio) { |
370 SDL_LockMutex(audio_mutex); | 175 SDL_LockMutex(audio_mutex); |
371 while (src->front_populated) { | 176 while (src->front_populated) { |
372 SDL_CondWait(src->cond, audio_mutex); | 177 SDL_CondWait(src->opaque, audio_mutex); |
373 } | 178 } |
374 int16_t *tmp = src->front; | 179 int16_t *tmp = src->front; |
375 src->front = src->back; | 180 src->front = src->back; |
376 src->back = tmp; | 181 src->back = tmp; |
377 src->front_populated = 1; | 182 src->front_populated = 1; |
388 SDL_PauseAudio(0); | 193 SDL_PauseAudio(0); |
389 } | 194 } |
390 } | 195 } |
391 } | 196 } |
392 | 197 |
393 static int16_t lowpass_sample(audio_source *src, int16_t last, int16_t current) | |
394 { | |
395 int32_t tmp = current * src->lowpass_alpha + last * (0x10000 - src->lowpass_alpha); | |
396 current = tmp >> 16; | |
397 return current; | |
398 } | |
399 | |
400 static void interp_sample(audio_source *src, int16_t last, int16_t current) | |
401 { | |
402 int64_t tmp = last * ((src->buffer_fraction << 16) / src->buffer_inc); | |
403 tmp += current * (0x10000 - ((src->buffer_fraction << 16) / src->buffer_inc)); | |
404 src->back[src->buffer_pos++] = tmp >> 16; | |
405 } | |
406 | |
407 void render_put_mono_sample(audio_source *src, int16_t value) | |
408 { | |
409 value = lowpass_sample(src, src->last_left, value); | |
410 src->buffer_fraction += src->buffer_inc; | |
411 uint32_t base = sync_to_audio ? 0 : src->read_end; | |
412 while (src->buffer_fraction > BUFFER_INC_RES) | |
413 { | |
414 src->buffer_fraction -= BUFFER_INC_RES; | |
415 interp_sample(src, src->last_left, value); | |
416 | |
417 if (((src->buffer_pos - base) & src->mask) >= sync_samples) { | |
418 do_audio_ready(src); | |
419 } | |
420 src->buffer_pos &= src->mask; | |
421 } | |
422 src->last_left = value; | |
423 } | |
424 | |
425 void render_put_stereo_sample(audio_source *src, int16_t left, int16_t right) | |
426 { | |
427 left = lowpass_sample(src, src->last_left, left); | |
428 right = lowpass_sample(src, src->last_right, right); | |
429 src->buffer_fraction += src->buffer_inc; | |
430 uint32_t base = sync_to_audio ? 0 : src->read_end; | |
431 while (src->buffer_fraction > BUFFER_INC_RES) | |
432 { | |
433 src->buffer_fraction -= BUFFER_INC_RES; | |
434 | |
435 interp_sample(src, src->last_left, left); | |
436 interp_sample(src, src->last_right, right); | |
437 | |
438 if (((src->buffer_pos - base) & src->mask)/2 >= sync_samples) { | |
439 do_audio_ready(src); | |
440 } | |
441 src->buffer_pos &= src->mask; | |
442 } | |
443 src->last_left = left; | |
444 src->last_right = right; | |
445 } | |
446 | |
447 static SDL_Joystick * joysticks[MAX_JOYSTICKS]; | 198 static SDL_Joystick * joysticks[MAX_JOYSTICKS]; |
448 static int joystick_sdl_index[MAX_JOYSTICKS]; | 199 static int joystick_sdl_index[MAX_JOYSTICKS]; |
200 static uint8_t joystick_index_locked[MAX_JOYSTICKS]; | |
449 | 201 |
450 int render_width() | 202 int render_width() |
451 { | 203 { |
452 return main_width; | 204 return main_width; |
453 } | 205 } |
633 static void render_alloc_surfaces() | 385 static void render_alloc_surfaces() |
634 { | 386 { |
635 if (texture_init) { | 387 if (texture_init) { |
636 return; | 388 return; |
637 } | 389 } |
638 sdl_textures= malloc(sizeof(SDL_Texture *) * 2); | 390 sdl_textures= calloc(sizeof(SDL_Texture *), 3); |
639 num_textures = 2; | 391 num_textures = 3; |
640 texture_init = 1; | 392 texture_init = 1; |
641 #ifndef DISABLE_OPENGL | 393 #ifndef DISABLE_OPENGL |
642 if (render_gl) { | 394 if (render_gl) { |
643 sdl_textures[0] = sdl_textures[1] = NULL; | |
644 gl_setup(); | 395 gl_setup(); |
645 } else { | 396 } else { |
646 #endif | 397 #endif |
647 tern_val def = {.ptrval = "linear"}; | 398 tern_val def = {.ptrval = "linear"}; |
648 char *scaling = tern_find_path_default(config, "video\0scaling\0", def, TVAL_PTR).ptrval; | 399 char *scaling = tern_find_path_default(config, "video\0scaling\0", def, TVAL_PTR).ptrval; |
887 } | 638 } |
888 } | 639 } |
889 return -1; | 640 return -1; |
890 } | 641 } |
891 | 642 |
643 static int lowest_unlocked_joystick_index(void) | |
644 { | |
645 for (int i = 0; i < MAX_JOYSTICKS; i++) { | |
646 if (!joystick_index_locked[i]) { | |
647 return i; | |
648 } | |
649 } | |
650 return -1; | |
651 } | |
652 | |
892 SDL_Joystick *render_get_joystick(int index) | 653 SDL_Joystick *render_get_joystick(int index) |
893 { | 654 { |
894 if (index >= MAX_JOYSTICKS) { | 655 if (index >= MAX_JOYSTICKS) { |
895 return NULL; | 656 return NULL; |
896 } | 657 } |
908 return guid_string; | 669 return guid_string; |
909 } | 670 } |
910 | 671 |
911 SDL_GameController *render_get_controller(int index) | 672 SDL_GameController *render_get_controller(int index) |
912 { | 673 { |
913 if (index >= MAX_JOYSTICKS) { | 674 if (index >= MAX_JOYSTICKS || !joysticks[index]) { |
914 return NULL; | 675 return NULL; |
915 } | 676 } |
916 return SDL_GameControllerOpen(joystick_sdl_index[index]); | 677 return SDL_GameControllerOpen(joystick_sdl_index[index]); |
678 } | |
679 | |
680 static uint8_t gc_events_enabled; | |
681 static SDL_GameController *controllers[MAX_JOYSTICKS]; | |
682 void render_enable_gamepad_events(uint8_t enabled) | |
683 { | |
684 if (enabled != gc_events_enabled) { | |
685 gc_events_enabled = enabled; | |
686 for (int i = 0; i < MAX_JOYSTICKS; i++) { | |
687 if (enabled) { | |
688 controllers[i] = render_get_controller(i); | |
689 } else if (controllers[i]) { | |
690 SDL_GameControllerClose(controllers[i]); | |
691 controllers[i] = NULL; | |
692 } | |
693 } | |
694 } | |
917 } | 695 } |
918 | 696 |
919 static uint32_t overscan_top[NUM_VID_STD] = {2, 21}; | 697 static uint32_t overscan_top[NUM_VID_STD] = {2, 21}; |
920 static uint32_t overscan_bot[NUM_VID_STD] = {1, 17}; | 698 static uint32_t overscan_bot[NUM_VID_STD] = {1, 17}; |
921 static uint32_t overscan_left[NUM_VID_STD] = {13, 13}; | 699 static uint32_t overscan_left[NUM_VID_STD] = {13, 13}; |
922 static uint32_t overscan_right[NUM_VID_STD] = {14, 14}; | 700 static uint32_t overscan_right[NUM_VID_STD] = {14, 14}; |
923 static vid_std video_standard = VID_NTSC; | 701 static vid_std video_standard = VID_NTSC; |
924 static uint8_t need_ui_fb_resize; | 702 static uint8_t need_ui_fb_resize; |
703 | |
704 int lock_joystick_index(int joystick, int desired_index) | |
705 { | |
706 if (desired_index < 0) { | |
707 desired_index = lowest_unlocked_joystick_index(); | |
708 if (desired_index < 0 || desired_index >= joystick) { | |
709 return joystick; | |
710 } | |
711 } | |
712 SDL_Joystick *tmp_joy = joysticks[joystick]; | |
713 int tmp_index = joystick_sdl_index[joystick]; | |
714 joysticks[joystick] = joysticks[desired_index]; | |
715 joystick_sdl_index[joystick] = joystick_sdl_index[desired_index]; | |
716 joystick_index_locked[joystick] = joystick_sdl_index[desired_index]; | |
717 joysticks[desired_index] = tmp_joy; | |
718 joystick_sdl_index[desired_index] = tmp_index; | |
719 joystick_index_locked[desired_index] = 1; | |
720 //update bindings as the controllers being swapped may have different mappings | |
721 handle_joy_added(desired_index); | |
722 if (joysticks[joystick]) { | |
723 handle_joy_added(joystick); | |
724 } | |
725 return desired_index; | |
726 } | |
925 | 727 |
926 static int32_t handle_event(SDL_Event *event) | 728 static int32_t handle_event(SDL_Event *event) |
927 { | 729 { |
928 if (custom_event_handler) { | 730 if (custom_event_handler) { |
929 custom_event_handler(event); | 731 custom_event_handler(event); |
937 break; | 739 break; |
938 case SDL_JOYBUTTONDOWN: | 740 case SDL_JOYBUTTONDOWN: |
939 handle_joydown(find_joystick_index(event->jbutton.which), event->jbutton.button); | 741 handle_joydown(find_joystick_index(event->jbutton.which), event->jbutton.button); |
940 break; | 742 break; |
941 case SDL_JOYBUTTONUP: | 743 case SDL_JOYBUTTONUP: |
942 handle_joyup(find_joystick_index(event->jbutton.which), event->jbutton.button); | 744 handle_joyup(lock_joystick_index(find_joystick_index(event->jbutton.which), -1), event->jbutton.button); |
943 break; | 745 break; |
944 case SDL_JOYHATMOTION: | 746 case SDL_JOYHATMOTION: |
945 handle_joy_dpad(find_joystick_index(event->jhat.which), event->jhat.hat, event->jhat.value); | 747 handle_joy_dpad(lock_joystick_index(find_joystick_index(event->jhat.which), -1), event->jhat.hat, event->jhat.value); |
946 break; | 748 break; |
947 case SDL_JOYAXISMOTION: | 749 case SDL_JOYAXISMOTION: |
948 handle_joy_axis(find_joystick_index(event->jaxis.which), event->jaxis.axis, event->jaxis.value); | 750 handle_joy_axis(lock_joystick_index(find_joystick_index(event->jaxis.which), -1), event->jaxis.axis, event->jaxis.value); |
949 break; | 751 break; |
950 case SDL_JOYDEVICEADDED: | 752 case SDL_JOYDEVICEADDED: |
951 if (event->jdevice.which < MAX_JOYSTICKS) { | 753 if (event->jdevice.which < MAX_JOYSTICKS) { |
952 int index = lowest_unused_joystick_index(); | 754 int index = lowest_unused_joystick_index(); |
953 if (index >= 0) { | 755 if (index >= 0) { |
954 SDL_Joystick * joy = joysticks[index] = SDL_JoystickOpen(event->jdevice.which); | 756 SDL_Joystick * joy = joysticks[index] = SDL_JoystickOpen(event->jdevice.which); |
955 joystick_sdl_index[index] = event->jdevice.which; | 757 joystick_sdl_index[index] = event->jdevice.which; |
758 joystick_index_locked[index] = 0; | |
759 if (gc_events_enabled) { | |
760 controllers[index] = SDL_GameControllerOpen(event->jdevice.which); | |
761 } | |
956 if (joy) { | 762 if (joy) { |
957 debug_message("Joystick %d added: %s\n", index, SDL_JoystickName(joy)); | 763 debug_message("Joystick %d added: %s\n", index, SDL_JoystickName(joy)); |
958 debug_message("\tNum Axes: %d\n\tNum Buttons: %d\n\tNum Hats: %d\n", SDL_JoystickNumAxes(joy), SDL_JoystickNumButtons(joy), SDL_JoystickNumHats(joy)); | 764 debug_message("\tNum Axes: %d\n\tNum Buttons: %d\n\tNum Hats: %d\n", SDL_JoystickNumAxes(joy), SDL_JoystickNumButtons(joy), SDL_JoystickNumHats(joy)); |
959 handle_joy_added(index); | 765 handle_joy_added(index); |
960 } | 766 } |
964 case SDL_JOYDEVICEREMOVED: { | 770 case SDL_JOYDEVICEREMOVED: { |
965 int index = find_joystick_index(event->jdevice.which); | 771 int index = find_joystick_index(event->jdevice.which); |
966 if (index >= 0) { | 772 if (index >= 0) { |
967 SDL_JoystickClose(joysticks[index]); | 773 SDL_JoystickClose(joysticks[index]); |
968 joysticks[index] = NULL; | 774 joysticks[index] = NULL; |
775 if (controllers[index]) { | |
776 SDL_GameControllerClose(controllers[index]); | |
777 controllers[index] = NULL; | |
778 } | |
969 debug_message("Joystick %d removed\n", index); | 779 debug_message("Joystick %d removed\n", index); |
970 } else { | 780 } else { |
971 debug_message("Failed to find removed joystick with instance ID: %d\n", index); | 781 debug_message("Failed to find removed joystick with instance ID: %d\n", index); |
972 } | 782 } |
973 break; | 783 break; |
1051 static int source_hz; | 861 static int source_hz; |
1052 static int source_frame; | 862 static int source_frame; |
1053 static int source_frame_count; | 863 static int source_frame_count; |
1054 static int frame_repeat[60]; | 864 static int frame_repeat[60]; |
1055 | 865 |
866 static uint32_t sample_rate; | |
1056 static void init_audio() | 867 static void init_audio() |
1057 { | 868 { |
1058 SDL_AudioSpec desired, actual; | 869 SDL_AudioSpec desired, actual; |
1059 char * rate_str = tern_find_path(config, "audio\0rate\0", TVAL_PTR).ptrval; | 870 char * rate_str = tern_find_path(config, "audio\0rate\0", TVAL_PTR).ptrval; |
1060 int rate = rate_str ? atoi(rate_str) : 0; | 871 int rate = rate_str ? atoi(rate_str) : 0; |
1075 desired.userdata = NULL; | 886 desired.userdata = NULL; |
1076 | 887 |
1077 if (SDL_OpenAudio(&desired, &actual) < 0) { | 888 if (SDL_OpenAudio(&desired, &actual) < 0) { |
1078 fatal_error("Unable to open SDL audio: %s\n", SDL_GetError()); | 889 fatal_error("Unable to open SDL audio: %s\n", SDL_GetError()); |
1079 } | 890 } |
1080 buffer_samples = actual.samples; | |
1081 sample_rate = actual.freq; | 891 sample_rate = actual.freq; |
1082 output_channels = actual.channels; | |
1083 debug_message("Initialized audio at frequency %d with a %d sample buffer, ", actual.freq, actual.samples); | 892 debug_message("Initialized audio at frequency %d with a %d sample buffer, ", actual.freq, actual.samples); |
1084 sample_size = SDL_AUDIO_BITSIZE(actual.format) / 8; | 893 render_audio_format format = RENDER_AUDIO_UNKNOWN; |
1085 if (actual.format == AUDIO_S16SYS) { | 894 if (actual.format == AUDIO_S16SYS) { |
1086 debug_message("signed 16-bit int format\n"); | 895 debug_message("signed 16-bit int format\n"); |
1087 convert = convert_s16; | 896 format = RENDER_AUDIO_S16; |
1088 mix_buf = calloc(output_channels * buffer_samples, sizeof(float)); | |
1089 } else if (actual.format == AUDIO_F32SYS) { | 897 } else if (actual.format == AUDIO_F32SYS) { |
1090 debug_message("32-bit float format\n"); | 898 debug_message("32-bit float format\n"); |
1091 convert = clamp_f32; | 899 format = RENDER_AUDIO_FLOAT; |
1092 mix_buf = NULL; | |
1093 } else { | 900 } else { |
1094 debug_message("unsupported format %X\n", actual.format); | 901 debug_message("unsupported format %X\n", actual.format); |
1095 warning("Unsupported audio sample format: %X\n", actual.format); | 902 warning("Unsupported audio sample format: %X\n", actual.format); |
1096 convert = convert_null; | 903 } |
1097 mix_buf = calloc(output_channels * buffer_samples, sizeof(float)); | 904 render_audio_initialized(format, actual.freq, actual.channels, actual.samples, SDL_AUDIO_BITSIZE(actual.format) / 8); |
1098 } | |
1099 char * gain_str = tern_find_path(config, "audio\0gain\0", TVAL_PTR).ptrval; | |
1100 overall_gain_mult = db_to_mult(gain_str ? atof(gain_str) : 0.0f); | |
1101 } | 905 } |
1102 | 906 |
1103 void window_setup(void) | 907 void window_setup(void) |
1104 { | 908 { |
1105 uint32_t flags = SDL_WINDOW_RESIZABLE; | 909 uint32_t flags = SDL_WINDOW_RESIZABLE; |
1290 | 1094 |
1291 atexit(render_quit); | 1095 atexit(render_quit); |
1292 } | 1096 } |
1293 #include<unistd.h> | 1097 #include<unistd.h> |
1294 static int in_toggle; | 1098 static int in_toggle; |
1295 static void update_source(audio_source *src, double rc, uint8_t sync_changed) | |
1296 { | |
1297 double alpha = src->dt / (src->dt + rc); | |
1298 int32_t lowpass_alpha = (int32_t)(((double)0x10000) * alpha); | |
1299 src->lowpass_alpha = lowpass_alpha; | |
1300 if (sync_changed) { | |
1301 uint32_t alloc_size = sync_to_audio ? src->num_channels * buffer_samples : nearest_pow2(min_buffered * 4 * src->num_channels); | |
1302 src->back = realloc(src->back, alloc_size * sizeof(int16_t)); | |
1303 if (sync_to_audio) { | |
1304 src->front = malloc(alloc_size * sizeof(int16_t)); | |
1305 } else { | |
1306 free(src->front); | |
1307 src->front = src->back; | |
1308 } | |
1309 src->mask = sync_to_audio ? 0xFFFFFFFF : alloc_size-1; | |
1310 src->read_start = 0; | |
1311 src->read_end = sync_to_audio ? buffer_samples * src->num_channels : 0; | |
1312 src->buffer_pos = 0; | |
1313 } | |
1314 } | |
1315 | 1099 |
1316 void render_config_updated(void) | 1100 void render_config_updated(void) |
1317 { | 1101 { |
1318 uint8_t old_sync_to_audio = sync_to_audio; | 1102 uint8_t old_sync_to_audio = sync_to_audio; |
1319 | 1103 |
1376 render_close_audio(); | 1160 render_close_audio(); |
1377 quitting = 0; | 1161 quitting = 0; |
1378 init_audio(); | 1162 init_audio(); |
1379 render_set_video_standard(video_standard); | 1163 render_set_video_standard(video_standard); |
1380 | 1164 |
1381 double lowpass_cutoff = get_lowpass_cutoff(config); | |
1382 double rc = (1.0 / lowpass_cutoff) / (2.0 * M_PI); | |
1383 lock_audio(); | |
1384 for (uint8_t i = 0; i < num_audio_sources; i++) | |
1385 { | |
1386 update_source(audio_sources[i], rc, old_sync_to_audio != sync_to_audio); | |
1387 } | |
1388 unlock_audio(); | |
1389 for (uint8_t i = 0; i < num_inactive_audio_sources; i++) | |
1390 { | |
1391 update_source(inactive_audio_sources[i], rc, old_sync_to_audio != sync_to_audio); | |
1392 } | |
1393 drain_events(); | 1165 drain_events(); |
1394 in_toggle = 0; | 1166 in_toggle = 0; |
1395 if (!was_paused) { | 1167 if (!was_paused) { |
1396 SDL_PauseAudio(0); | 1168 SDL_PauseAudio(0); |
1397 } | 1169 } |
1398 } | 1170 } |
1399 | 1171 |
1400 SDL_Window *render_get_window(void) | 1172 SDL_Window *render_get_window(void) |
1401 { | 1173 { |
1402 return main_window; | 1174 return main_window; |
1175 } | |
1176 | |
1177 uint32_t render_audio_syncs_per_sec(void) | |
1178 { | |
1179 //sync samples with audio thread approximately every 8 lines when doing sync to video | |
1180 return sync_to_audio ? 0 : source_hz * (video_standard == VID_PAL ? 313 : 262) / 8; | |
1403 } | 1181 } |
1404 | 1182 |
1405 void render_set_video_standard(vid_std std) | 1183 void render_set_video_standard(vid_std std) |
1406 { | 1184 { |
1407 video_standard = std; | 1185 video_standard = std; |
1429 frame_repeat[source_hz-1] += display_hz - dst_frames; | 1207 frame_repeat[source_hz-1] += display_hz - dst_frames; |
1430 } | 1208 } |
1431 } | 1209 } |
1432 source_frame = 0; | 1210 source_frame = 0; |
1433 source_frame_count = frame_repeat[0]; | 1211 source_frame_count = frame_repeat[0]; |
1434 //sync samples with audio thread approximately every 8 lines | |
1435 sync_samples = sync_to_audio ? buffer_samples : 8 * sample_rate / (source_hz * (std == VID_PAL ? 313 : 262)); | |
1436 max_repeat++; | 1212 max_repeat++; |
1437 min_buffered = (((float)max_repeat * (float)sample_rate/(float)source_hz)/* / (float)buffer_samples*/);// + 0.9999; | 1213 min_buffered = (((float)max_repeat * (float)sample_rate/(float)source_hz)/* / (float)buffer_samples*/);// + 0.9999; |
1438 //min_buffered *= buffer_samples; | 1214 //min_buffered *= buffer_samples; |
1439 debug_message("Min samples buffered before audio start: %d\n", min_buffered); | 1215 debug_message("Min samples buffered before audio start: %d\n", min_buffered); |
1440 max_adjust = BASE_MAX_ADJUST / source_hz; | 1216 max_adjust = BASE_MAX_ADJUST / source_hz; |
1519 if (render_gl && which <= FRAMEBUFFER_EVEN) { | 1295 if (render_gl && which <= FRAMEBUFFER_EVEN) { |
1520 *pitch = LINEBUF_SIZE * sizeof(uint32_t); | 1296 *pitch = LINEBUF_SIZE * sizeof(uint32_t); |
1521 return texture_buf; | 1297 return texture_buf; |
1522 } else { | 1298 } else { |
1523 #endif | 1299 #endif |
1524 if (which == FRAMEBUFFER_UI && which >= num_textures) { | |
1525 sdl_textures = realloc(sdl_textures, sizeof(*sdl_textures) * (FRAMEBUFFER_UI + 1)); | |
1526 for (; num_textures <= FRAMEBUFFER_UI; num_textures++) | |
1527 { | |
1528 sdl_textures[num_textures] = NULL; | |
1529 } | |
1530 } | |
1531 if (which == FRAMEBUFFER_UI && !sdl_textures[which]) { | 1300 if (which == FRAMEBUFFER_UI && !sdl_textures[which]) { |
1532 sdl_textures[which] = SDL_CreateTexture(main_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, main_width, main_height); | 1301 sdl_textures[which] = SDL_CreateTexture(main_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, main_width, main_height); |
1533 } | 1302 } |
1534 if (which >= num_textures) { | 1303 if (which >= num_textures) { |
1535 warning("Request for invalid framebuffer number %d\n", which); | 1304 warning("Request for invalid framebuffer number %d\n", which); |
1591 screenshot_file = fopen(screenshot_path, "wb"); | 1360 screenshot_file = fopen(screenshot_path, "wb"); |
1592 if (screenshot_file) { | 1361 if (screenshot_file) { |
1593 #ifndef DISABLE_ZLIB | 1362 #ifndef DISABLE_ZLIB |
1594 ext = path_extension(screenshot_path); | 1363 ext = path_extension(screenshot_path); |
1595 #endif | 1364 #endif |
1596 info_message("Saving screenshot to %s\n", screenshot_path); | 1365 debug_message("Saving screenshot to %s\n", screenshot_path); |
1597 } else { | 1366 } else { |
1598 warning("Failed to open screenshot file %s for writing\n", screenshot_path); | 1367 warning("Failed to open screenshot file %s for writing\n", screenshot_path); |
1599 } | 1368 } |
1600 free(screenshot_path); | 1369 free(screenshot_path); |
1601 screenshot_path = NULL; | 1370 screenshot_path = NULL; |
1746 } else if (local_cur_min < min_buffered / 2) { | 1515 } else if (local_cur_min < min_buffered / 2) { |
1747 adjust_ratio = max_adjust; | 1516 adjust_ratio = max_adjust; |
1748 } | 1517 } |
1749 if (adjust_ratio != 0.0f) { | 1518 if (adjust_ratio != 0.0f) { |
1750 average_change = 0; | 1519 average_change = 0; |
1751 for (uint8_t i = 0; i < num_audio_sources; i++) | 1520 render_audio_adjust_speed(adjust_ratio); |
1752 { | 1521 |
1753 audio_sources[i]->buffer_inc = ((double)audio_sources[i]->buffer_inc) + ((double)audio_sources[i]->buffer_inc) * adjust_ratio + 0.5; | |
1754 } | |
1755 } | 1522 } |
1756 while (source_frame_count > 0) | 1523 while (source_frame_count > 0) |
1757 { | 1524 { |
1758 render_update_display(); | 1525 render_update_display(); |
1759 source_frame_count--; | 1526 source_frame_count--; |
1847 uint32_t render_overscan_top() | 1614 uint32_t render_overscan_top() |
1848 { | 1615 { |
1849 return overscan_top[video_standard]; | 1616 return overscan_top[video_standard]; |
1850 } | 1617 } |
1851 | 1618 |
1852 void render_wait_quit(vdp_context * context) | 1619 uint32_t render_overscan_bot() |
1620 { | |
1621 return overscan_bot[video_standard]; | |
1622 } | |
1623 | |
1624 void render_wait_quit(void) | |
1853 { | 1625 { |
1854 SDL_Event event; | 1626 SDL_Event event; |
1855 while(SDL_WaitEvent(&event)) { | 1627 while(SDL_WaitEvent(&event)) { |
1856 switch (event.type) { | 1628 switch (event.type) { |
1857 case SDL_QUIT: | 1629 case SDL_QUIT: |
2015 drain_events(); | 1787 drain_events(); |
2016 in_toggle = 0; | 1788 in_toggle = 0; |
2017 need_ui_fb_resize = 1; | 1789 need_ui_fb_resize = 1; |
2018 } | 1790 } |
2019 | 1791 |
2020 uint32_t render_audio_buffer() | |
2021 { | |
2022 return buffer_samples; | |
2023 } | |
2024 | |
2025 uint32_t render_sample_rate() | |
2026 { | |
2027 return sample_rate; | |
2028 } | |
2029 | |
2030 void render_errorbox(char *title, char *message) | 1792 void render_errorbox(char *title, char *message) |
2031 { | 1793 { |
2032 SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title, message, NULL); | 1794 SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title, message, NULL); |
2033 } | 1795 } |
2034 | 1796 |