Mercurial > repos > blastem
diff 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 |
line wrap: on
line diff
--- a/render_sdl.c Thu Apr 18 22:06:47 2019 -0700 +++ b/render_sdl.c Sat Apr 18 11:42:53 2020 -0700 @@ -46,151 +46,41 @@ static uint32_t last_frame = 0; -static uint8_t output_channels; -static uint32_t buffer_samples, sample_rate; -static uint32_t missing_count; - static SDL_mutex * audio_mutex; static SDL_cond * audio_ready; static uint8_t quitting = 0; -struct audio_source { - SDL_cond *cond; - int16_t *front; - int16_t *back; - double dt; - uint64_t buffer_fraction; - uint64_t buffer_inc; - float gain_mult; - uint32_t buffer_pos; - uint32_t read_start; - uint32_t read_end; - uint32_t lowpass_alpha; - uint32_t mask; - int16_t last_left; - int16_t last_right; - uint8_t num_channels; - uint8_t front_populated; -}; - -static audio_source *audio_sources[8]; -static audio_source *inactive_audio_sources[8]; -static uint8_t num_audio_sources; -static uint8_t num_inactive_audio_sources; static uint8_t sync_to_audio; static uint32_t min_buffered; -static float overall_gain_mult, *mix_buf; -static int sample_size; -typedef void (*conv_func)(float *samples, void *vstream, int sample_count); - -static void convert_null(float *samples, void *vstream, int sample_count) -{ - memset(vstream, 0, sample_count * sample_size); -} - -static void convert_s16(float *samples, void *vstream, int sample_count) +uint32_t render_min_buffered(void) { - int16_t *stream = vstream; - for (int16_t *end = stream + sample_count; stream < end; stream++, samples++) - { - float sample = *samples; - int16_t out_sample; - if (sample >= 1.0f) { - out_sample = 0x7FFF; - } else if (sample <= -1.0f) { - out_sample = -0x8000; - } else { - out_sample = sample * 0x7FFF; - } - *stream = out_sample; - } + return min_buffered; } -static void clamp_f32(float *samples, void *vstream, int sample_count) +uint8_t render_is_audio_sync(void) { - for (; sample_count > 0; sample_count--, samples++) - { - float sample = *samples; - if (sample > 1.0f) { - sample = 1.0f; - } else if (sample < -1.0f) { - sample = -1.0f; - } - *samples = sample; - } + return sync_to_audio; } -static int32_t mix_f32(audio_source *audio, float *stream, int samples) +void render_buffer_consumed(audio_source *src) { - float *end = stream + samples; - int16_t *src = audio->front; - uint32_t i = audio->read_start; - uint32_t i_end = audio->read_end; - float *cur = stream; - float gain_mult = audio->gain_mult * overall_gain_mult; - size_t first_add = output_channels > 1 ? 1 : 0, second_add = output_channels > 1 ? output_channels - 1 : 1; - if (audio->num_channels == 1) { - while (cur < end && i != i_end) - { - *cur += gain_mult * ((float)src[i]) / 0x7FFF; - cur += first_add; - *cur += gain_mult * ((float)src[i++]) / 0x7FFF; - cur += second_add; - i &= audio->mask; - } - } else { - while(cur < end && i != i_end) - { - *cur += gain_mult * ((float)src[i++]) / 0x7FFF; - cur += first_add; - *cur += gain_mult * ((float)src[i++]) / 0x7FFF; - cur += second_add; - i &= audio->mask; - } - } - if (!sync_to_audio) { - audio->read_start = i; - } - if (cur != end) { - 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); - return (cur-end)/2; - } else { - return ((i_end - i) & audio->mask) / audio->num_channels; - } + SDL_CondSignal(src->opaque); } -static conv_func convert; - static void audio_callback(void * userdata, uint8_t *byte_stream, int len) { - uint8_t num_populated; SDL_LockMutex(audio_mutex); + uint8_t all_ready; do { - num_populated = 0; - for (uint8_t i = 0; i < num_audio_sources; i++) - { - if (audio_sources[i]->front_populated) { - num_populated++; - } - } - if (!quitting && num_populated < num_audio_sources) { - fflush(stdout); + all_ready = all_sources_ready(); + if (!quitting && !all_ready) { SDL_CondWait(audio_ready, audio_mutex); } - } while(!quitting && num_populated < num_audio_sources); - int samples = len / sample_size; - float *mix_dest = mix_buf ? mix_buf : (float *)byte_stream; - memset(mix_dest, 0, samples * sizeof(float)); + } while(!quitting && !all_ready); if (!quitting) { - for (uint8_t i = 0; i < num_audio_sources; i++) - { - mix_f32(audio_sources[i], mix_dest, samples); - audio_sources[i]->front_populated = 0; - SDL_CondSignal(audio_sources[i]->cond); - } + mix_and_convert(byte_stream, len, NULL); } - convert(mix_dest, byte_stream, samples); SDL_UnlockMutex(audio_mutex); } @@ -208,23 +98,10 @@ //underflow last frame, but main thread hasn't gotten a chance to call SDL_PauseAudio yet return; } - cur_min_buffered = 0x7FFFFFFF; - min_remaining_buffer = 0xFFFFFFFF; - float *mix_dest = mix_buf ? mix_buf : (float *)byte_stream; - int samples = len / sample_size; - memset(mix_dest, 0, samples * sizeof(float)); - for (uint8_t i = 0; i < num_audio_sources; i++) - { - - int32_t buffered = mix_f32(audio_sources[i], mix_dest, samples); - cur_min_buffered = buffered < cur_min_buffered ? buffered : cur_min_buffered; - uint32_t remaining = (audio_sources[i]->mask + 1)/audio_sources[i]->num_channels - buffered; - min_remaining_buffer = remaining < min_remaining_buffer ? remaining : min_remaining_buffer; - } - convert(mix_dest, byte_stream, samples); + cur_min_buffered = mix_and_convert(byte_stream, len, &min_remaining_buffer); } -static void lock_audio() +void render_lock_audio() { if (sync_to_audio) { SDL_LockMutex(audio_mutex); @@ -233,7 +110,7 @@ } } -static void unlock_audio() +void render_unlock_audio() { if (sync_to_audio) { SDL_UnlockMutex(audio_mutex); @@ -249,127 +126,55 @@ SDL_CondSignal(audio_ready); SDL_UnlockMutex(audio_mutex); SDL_CloseAudio(); + /* + FIXME: move this to render_audio.c if (mix_buf) { free(mix_buf); mix_buf = NULL; } + */ } -#define BUFFER_INC_RES 0x40000000UL - -void render_audio_adjust_clock(audio_source *src, uint64_t master_clock, uint64_t sample_divider) +void *render_new_audio_opaque(void) { - src->buffer_inc = ((BUFFER_INC_RES * (uint64_t)sample_rate) / master_clock) * sample_divider; + return SDL_CreateCond(); } -audio_source *render_audio_source(uint64_t master_clock, uint64_t sample_divider, uint8_t channels) +void render_free_audio_opaque(void *opaque) { - audio_source *ret = NULL; - uint32_t alloc_size = sync_to_audio ? channels * buffer_samples : nearest_pow2(min_buffered * 4 * channels); - lock_audio(); - if (num_audio_sources < 8) { - ret = malloc(sizeof(audio_source)); - ret->back = malloc(alloc_size * sizeof(int16_t)); - ret->front = sync_to_audio ? malloc(alloc_size * sizeof(int16_t)) : ret->back; - ret->front_populated = 0; - ret->cond = SDL_CreateCond(); - ret->num_channels = channels; - audio_sources[num_audio_sources++] = ret; - } - unlock_audio(); - if (!ret) { - fatal_error("Too many audio sources!"); - } else { - render_audio_adjust_clock(ret, master_clock, sample_divider); - double lowpass_cutoff = get_lowpass_cutoff(config); - double rc = (1.0 / lowpass_cutoff) / (2.0 * M_PI); - ret->dt = 1.0 / ((double)master_clock / (double)(sample_divider)); - double alpha = ret->dt / (ret->dt + rc); - ret->lowpass_alpha = (int32_t)(((double)0x10000) * alpha); - ret->buffer_pos = 0; - ret->buffer_fraction = 0; - ret->last_left = ret->last_right = 0; - ret->read_start = 0; - ret->read_end = sync_to_audio ? buffer_samples * channels : 0; - ret->mask = sync_to_audio ? 0xFFFFFFFF : alloc_size-1; - ret->gain_mult = 1.0f; - } + SDL_DestroyCond(opaque); +} + +void render_audio_created(audio_source *source) +{ if (sync_to_audio && SDL_GetAudioStatus() == SDL_AUDIO_PAUSED) { SDL_PauseAudio(0); } - return ret; -} - -static float db_to_mult(float gain) -{ - return powf(10.0f, gain/20.0f); -} - -void render_audio_source_gaindb(audio_source *src, float gain) -{ - src->gain_mult = db_to_mult(gain); } -void render_pause_source(audio_source *src) +void render_source_paused(audio_source *src, uint8_t remaining_sources) { - uint8_t need_pause = 0; - lock_audio(); - for (uint8_t i = 0; i < num_audio_sources; i++) - { - if (audio_sources[i] == src) { - audio_sources[i] = audio_sources[--num_audio_sources]; - if (sync_to_audio) { - SDL_CondSignal(audio_ready); - } - break; - } - } - if (!num_audio_sources) { - need_pause = 1; - } - unlock_audio(); - if (need_pause) { - SDL_PauseAudio(1); + if (sync_to_audio) { + SDL_CondSignal(audio_ready); } - inactive_audio_sources[num_inactive_audio_sources++] = src; + if (!remaining_sources) { + SDL_PauseAudio(0); + } } -void render_resume_source(audio_source *src) +void render_source_resumed(audio_source *src) { - lock_audio(); - if (num_audio_sources < 8) { - audio_sources[num_audio_sources++] = src; - } - unlock_audio(); - for (uint8_t i = 0; i < num_inactive_audio_sources; i++) - { - if (inactive_audio_sources[i] == src) { - inactive_audio_sources[i] = inactive_audio_sources[--num_inactive_audio_sources]; - } - } if (sync_to_audio) { SDL_PauseAudio(0); } } -void render_free_source(audio_source *src) -{ - render_pause_source(src); - - free(src->front); - if (sync_to_audio) { - free(src->back); - SDL_DestroyCond(src->cond); - } - free(src); -} -static uint32_t sync_samples; -static void do_audio_ready(audio_source *src) +void render_do_audio_ready(audio_source *src) { if (sync_to_audio) { SDL_LockMutex(audio_mutex); while (src->front_populated) { - SDL_CondWait(src->cond, audio_mutex); + SDL_CondWait(src->opaque, audio_mutex); } int16_t *tmp = src->front; src->front = src->back; @@ -390,62 +195,9 @@ } } -static int16_t lowpass_sample(audio_source *src, int16_t last, int16_t current) -{ - int32_t tmp = current * src->lowpass_alpha + last * (0x10000 - src->lowpass_alpha); - current = tmp >> 16; - return current; -} - -static void interp_sample(audio_source *src, int16_t last, int16_t current) -{ - int64_t tmp = last * ((src->buffer_fraction << 16) / src->buffer_inc); - tmp += current * (0x10000 - ((src->buffer_fraction << 16) / src->buffer_inc)); - src->back[src->buffer_pos++] = tmp >> 16; -} - -void render_put_mono_sample(audio_source *src, int16_t value) -{ - value = lowpass_sample(src, src->last_left, value); - src->buffer_fraction += src->buffer_inc; - uint32_t base = sync_to_audio ? 0 : src->read_end; - while (src->buffer_fraction > BUFFER_INC_RES) - { - src->buffer_fraction -= BUFFER_INC_RES; - interp_sample(src, src->last_left, value); - - if (((src->buffer_pos - base) & src->mask) >= sync_samples) { - do_audio_ready(src); - } - src->buffer_pos &= src->mask; - } - src->last_left = value; -} - -void render_put_stereo_sample(audio_source *src, int16_t left, int16_t right) -{ - left = lowpass_sample(src, src->last_left, left); - right = lowpass_sample(src, src->last_right, right); - src->buffer_fraction += src->buffer_inc; - uint32_t base = sync_to_audio ? 0 : src->read_end; - while (src->buffer_fraction > BUFFER_INC_RES) - { - src->buffer_fraction -= BUFFER_INC_RES; - - interp_sample(src, src->last_left, left); - interp_sample(src, src->last_right, right); - - if (((src->buffer_pos - base) & src->mask)/2 >= sync_samples) { - do_audio_ready(src); - } - src->buffer_pos &= src->mask; - } - src->last_left = left; - src->last_right = right; -} - static SDL_Joystick * joysticks[MAX_JOYSTICKS]; static int joystick_sdl_index[MAX_JOYSTICKS]; +static uint8_t joystick_index_locked[MAX_JOYSTICKS]; int render_width() { @@ -635,12 +387,11 @@ if (texture_init) { return; } - sdl_textures= malloc(sizeof(SDL_Texture *) * 2); - num_textures = 2; + sdl_textures= calloc(sizeof(SDL_Texture *), 3); + num_textures = 3; texture_init = 1; #ifndef DISABLE_OPENGL if (render_gl) { - sdl_textures[0] = sdl_textures[1] = NULL; gl_setup(); } else { #endif @@ -889,6 +640,16 @@ return -1; } +static int lowest_unlocked_joystick_index(void) +{ + for (int i = 0; i < MAX_JOYSTICKS; i++) { + if (!joystick_index_locked[i]) { + return i; + } + } + return -1; +} + SDL_Joystick *render_get_joystick(int index) { if (index >= MAX_JOYSTICKS) { @@ -910,12 +671,29 @@ SDL_GameController *render_get_controller(int index) { - if (index >= MAX_JOYSTICKS) { + if (index >= MAX_JOYSTICKS || !joysticks[index]) { return NULL; } return SDL_GameControllerOpen(joystick_sdl_index[index]); } +static uint8_t gc_events_enabled; +static SDL_GameController *controllers[MAX_JOYSTICKS]; +void render_enable_gamepad_events(uint8_t enabled) +{ + if (enabled != gc_events_enabled) { + gc_events_enabled = enabled; + for (int i = 0; i < MAX_JOYSTICKS; i++) { + if (enabled) { + controllers[i] = render_get_controller(i); + } else if (controllers[i]) { + SDL_GameControllerClose(controllers[i]); + controllers[i] = NULL; + } + } + } +} + static uint32_t overscan_top[NUM_VID_STD] = {2, 21}; static uint32_t overscan_bot[NUM_VID_STD] = {1, 17}; static uint32_t overscan_left[NUM_VID_STD] = {13, 13}; @@ -923,6 +701,30 @@ static vid_std video_standard = VID_NTSC; static uint8_t need_ui_fb_resize; +int lock_joystick_index(int joystick, int desired_index) +{ + if (desired_index < 0) { + desired_index = lowest_unlocked_joystick_index(); + if (desired_index < 0 || desired_index >= joystick) { + return joystick; + } + } + SDL_Joystick *tmp_joy = joysticks[joystick]; + int tmp_index = joystick_sdl_index[joystick]; + joysticks[joystick] = joysticks[desired_index]; + joystick_sdl_index[joystick] = joystick_sdl_index[desired_index]; + joystick_index_locked[joystick] = joystick_sdl_index[desired_index]; + joysticks[desired_index] = tmp_joy; + joystick_sdl_index[desired_index] = tmp_index; + joystick_index_locked[desired_index] = 1; + //update bindings as the controllers being swapped may have different mappings + handle_joy_added(desired_index); + if (joysticks[joystick]) { + handle_joy_added(joystick); + } + return desired_index; +} + static int32_t handle_event(SDL_Event *event) { if (custom_event_handler) { @@ -939,13 +741,13 @@ handle_joydown(find_joystick_index(event->jbutton.which), event->jbutton.button); break; case SDL_JOYBUTTONUP: - handle_joyup(find_joystick_index(event->jbutton.which), event->jbutton.button); + handle_joyup(lock_joystick_index(find_joystick_index(event->jbutton.which), -1), event->jbutton.button); break; case SDL_JOYHATMOTION: - handle_joy_dpad(find_joystick_index(event->jhat.which), event->jhat.hat, event->jhat.value); + handle_joy_dpad(lock_joystick_index(find_joystick_index(event->jhat.which), -1), event->jhat.hat, event->jhat.value); break; case SDL_JOYAXISMOTION: - handle_joy_axis(find_joystick_index(event->jaxis.which), event->jaxis.axis, event->jaxis.value); + handle_joy_axis(lock_joystick_index(find_joystick_index(event->jaxis.which), -1), event->jaxis.axis, event->jaxis.value); break; case SDL_JOYDEVICEADDED: if (event->jdevice.which < MAX_JOYSTICKS) { @@ -953,6 +755,10 @@ if (index >= 0) { SDL_Joystick * joy = joysticks[index] = SDL_JoystickOpen(event->jdevice.which); joystick_sdl_index[index] = event->jdevice.which; + joystick_index_locked[index] = 0; + if (gc_events_enabled) { + controllers[index] = SDL_GameControllerOpen(event->jdevice.which); + } if (joy) { debug_message("Joystick %d added: %s\n", index, SDL_JoystickName(joy)); debug_message("\tNum Axes: %d\n\tNum Buttons: %d\n\tNum Hats: %d\n", SDL_JoystickNumAxes(joy), SDL_JoystickNumButtons(joy), SDL_JoystickNumHats(joy)); @@ -966,6 +772,10 @@ if (index >= 0) { SDL_JoystickClose(joysticks[index]); joysticks[index] = NULL; + if (controllers[index]) { + SDL_GameControllerClose(controllers[index]); + controllers[index] = NULL; + } debug_message("Joystick %d removed\n", index); } else { debug_message("Failed to find removed joystick with instance ID: %d\n", index); @@ -1053,6 +863,7 @@ static int source_frame_count; static int frame_repeat[60]; +static uint32_t sample_rate; static void init_audio() { SDL_AudioSpec desired, actual; @@ -1077,27 +888,20 @@ if (SDL_OpenAudio(&desired, &actual) < 0) { fatal_error("Unable to open SDL audio: %s\n", SDL_GetError()); } - buffer_samples = actual.samples; sample_rate = actual.freq; - output_channels = actual.channels; debug_message("Initialized audio at frequency %d with a %d sample buffer, ", actual.freq, actual.samples); - sample_size = SDL_AUDIO_BITSIZE(actual.format) / 8; + render_audio_format format = RENDER_AUDIO_UNKNOWN; if (actual.format == AUDIO_S16SYS) { debug_message("signed 16-bit int format\n"); - convert = convert_s16; - mix_buf = calloc(output_channels * buffer_samples, sizeof(float)); + format = RENDER_AUDIO_S16; } else if (actual.format == AUDIO_F32SYS) { debug_message("32-bit float format\n"); - convert = clamp_f32; - mix_buf = NULL; + format = RENDER_AUDIO_FLOAT; } else { debug_message("unsupported format %X\n", actual.format); warning("Unsupported audio sample format: %X\n", actual.format); - convert = convert_null; - mix_buf = calloc(output_channels * buffer_samples, sizeof(float)); } - char * gain_str = tern_find_path(config, "audio\0gain\0", TVAL_PTR).ptrval; - overall_gain_mult = db_to_mult(gain_str ? atof(gain_str) : 0.0f); + render_audio_initialized(format, actual.freq, actual.channels, actual.samples, SDL_AUDIO_BITSIZE(actual.format) / 8); } void window_setup(void) @@ -1292,26 +1096,6 @@ } #include<unistd.h> static int in_toggle; -static void update_source(audio_source *src, double rc, uint8_t sync_changed) -{ - double alpha = src->dt / (src->dt + rc); - int32_t lowpass_alpha = (int32_t)(((double)0x10000) * alpha); - src->lowpass_alpha = lowpass_alpha; - if (sync_changed) { - uint32_t alloc_size = sync_to_audio ? src->num_channels * buffer_samples : nearest_pow2(min_buffered * 4 * src->num_channels); - src->back = realloc(src->back, alloc_size * sizeof(int16_t)); - if (sync_to_audio) { - src->front = malloc(alloc_size * sizeof(int16_t)); - } else { - free(src->front); - src->front = src->back; - } - src->mask = sync_to_audio ? 0xFFFFFFFF : alloc_size-1; - src->read_start = 0; - src->read_end = sync_to_audio ? buffer_samples * src->num_channels : 0; - src->buffer_pos = 0; - } -} void render_config_updated(void) { @@ -1378,18 +1162,6 @@ init_audio(); render_set_video_standard(video_standard); - double lowpass_cutoff = get_lowpass_cutoff(config); - double rc = (1.0 / lowpass_cutoff) / (2.0 * M_PI); - lock_audio(); - for (uint8_t i = 0; i < num_audio_sources; i++) - { - update_source(audio_sources[i], rc, old_sync_to_audio != sync_to_audio); - } - unlock_audio(); - for (uint8_t i = 0; i < num_inactive_audio_sources; i++) - { - update_source(inactive_audio_sources[i], rc, old_sync_to_audio != sync_to_audio); - } drain_events(); in_toggle = 0; if (!was_paused) { @@ -1402,6 +1174,12 @@ return main_window; } +uint32_t render_audio_syncs_per_sec(void) +{ + //sync samples with audio thread approximately every 8 lines when doing sync to video + return sync_to_audio ? 0 : source_hz * (video_standard == VID_PAL ? 313 : 262) / 8; +} + void render_set_video_standard(vid_std std) { video_standard = std; @@ -1431,8 +1209,6 @@ } source_frame = 0; source_frame_count = frame_repeat[0]; - //sync samples with audio thread approximately every 8 lines - sync_samples = sync_to_audio ? buffer_samples : 8 * sample_rate / (source_hz * (std == VID_PAL ? 313 : 262)); max_repeat++; min_buffered = (((float)max_repeat * (float)sample_rate/(float)source_hz)/* / (float)buffer_samples*/);// + 0.9999; //min_buffered *= buffer_samples; @@ -1521,13 +1297,6 @@ return texture_buf; } else { #endif - if (which == FRAMEBUFFER_UI && which >= num_textures) { - sdl_textures = realloc(sdl_textures, sizeof(*sdl_textures) * (FRAMEBUFFER_UI + 1)); - for (; num_textures <= FRAMEBUFFER_UI; num_textures++) - { - sdl_textures[num_textures] = NULL; - } - } if (which == FRAMEBUFFER_UI && !sdl_textures[which]) { sdl_textures[which] = SDL_CreateTexture(main_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, main_width, main_height); } @@ -1593,7 +1362,7 @@ #ifndef DISABLE_ZLIB ext = path_extension(screenshot_path); #endif - info_message("Saving screenshot to %s\n", screenshot_path); + debug_message("Saving screenshot to %s\n", screenshot_path); } else { warning("Failed to open screenshot file %s for writing\n", screenshot_path); } @@ -1748,10 +1517,8 @@ } if (adjust_ratio != 0.0f) { average_change = 0; - for (uint8_t i = 0; i < num_audio_sources; i++) - { - audio_sources[i]->buffer_inc = ((double)audio_sources[i]->buffer_inc) + ((double)audio_sources[i]->buffer_inc) * adjust_ratio + 0.5; - } + render_audio_adjust_speed(adjust_ratio); + } while (source_frame_count > 0) { @@ -1849,7 +1616,12 @@ return overscan_top[video_standard]; } -void render_wait_quit(vdp_context * context) +uint32_t render_overscan_bot() +{ + return overscan_bot[video_standard]; +} + +void render_wait_quit(void) { SDL_Event event; while(SDL_WaitEvent(&event)) { @@ -2017,16 +1789,6 @@ need_ui_fb_resize = 1; } -uint32_t render_audio_buffer() -{ - return buffer_samples; -} - -uint32_t render_sample_rate() -{ - return sample_rate; -} - void render_errorbox(char *title, char *message) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title, message, NULL);