@@ -55,14 +55,19 @@ static int32_t samples_per_frame = 0;
5555static int32_t samplerate = (((SNES_CLOCK_SPEED * 6 ) / (32 * ONE_APU_CYCLE )));
5656
5757static unsigned frameskip_type = 0 ;
58+ static unsigned frameskip_threshold = 0 ;
5859static uint16_t frameskip_counter = 0 ;
60+
5961static bool retro_audio_buff_active = false;
6062static unsigned retro_audio_buff_occupancy = 0 ;
6163static bool retro_audio_buff_underrun = false;
6264/* Maximum number of consecutive frames that
6365 * can be skipped */
6466#define FRAMESKIP_MAX 30
6567
68+ static unsigned retro_audio_latency = 0 ;
69+ static bool update_audio_latency = false;
70+
6671#ifdef PERF_TEST
6772#define RETRO_PERFORMANCE_INIT (name ) \
6873 retro_perf_tick_t current_ticks; \
@@ -157,11 +162,34 @@ static void retro_set_audio_buff_status_cb(void)
157162 retro_audio_buff_active = false;
158163 retro_audio_buff_occupancy = 0 ;
159164 retro_audio_buff_underrun = false;
165+ retro_audio_latency = 0 ;
166+ }
167+ else
168+ {
169+ /* Frameskip is enabled - increase frontend
170+ * audio latency to minimise potential
171+ * buffer underruns */
172+ uint32_t frame_time_usec = Settings .FrameTime ;
173+
174+ if (Settings .ForceNTSC )
175+ frame_time_usec = Settings .FrameTimeNTSC ;
176+ if (Settings .ForcePAL )
177+ frame_time_usec = Settings .FrameTimePAL ;
178+
179+ /* Set latency to 6x current frame time... */
180+ retro_audio_latency = (unsigned )(6 * frame_time_usec / 1000 );
181+
182+ /* ...then round up to nearest multiple of 32 */
183+ retro_audio_latency = (retro_audio_latency + 0x1F ) & ~0x1F ;
160184 }
161185 }
162186 else
163- environ_cb (RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK ,
164- NULL );
187+ {
188+ environ_cb (RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK , NULL );
189+ retro_audio_latency = 0 ;
190+ }
191+
192+ update_audio_latency = true;
165193}
166194
167195void S9xDeinitDisplay (void )
@@ -383,7 +411,16 @@ void retro_deinit(void)
383411 perf_cb .perf_log ();
384412#endif
385413
414+ /* Reset globals (required for static builds) */
386415 libretro_supports_bitmasks = false;
416+ frameskip_type = 0 ;
417+ frameskip_threshold = 0 ;
418+ frameskip_counter = 0 ;
419+ retro_audio_buff_active = false;
420+ retro_audio_buff_occupancy = 0 ;
421+ retro_audio_buff_underrun = false;
422+ retro_audio_latency = 0 ;
423+ update_audio_latency = false;
387424}
388425
389426uint32_t S9xReadJoypad (int32_t port )
@@ -423,14 +460,19 @@ uint32_t S9xReadJoypad(int32_t port)
423460 return joypad ;
424461}
425462
426- static void check_variables (void )
463+ static void check_variables (bool first_run )
427464{
428465 struct retro_variable var ;
466+ bool prev_force_ntsc ;
467+ bool prev_force_pal ;
429468 bool prev_frameskip_type ;
430469
431470 var .key = "catsfc_VideoMode" ;
432471 var .value = NULL ;
433472
473+ prev_force_ntsc = Settings .ForceNTSC ;
474+ prev_force_pal = Settings .ForcePAL ;
475+
434476 if (environ_cb (RETRO_ENVIRONMENT_GET_VARIABLE , & var ) && var .value )
435477 {
436478 Settings .ForceNTSC = !strcmp (var .value , "NTSC" );
@@ -447,14 +489,17 @@ static void check_variables(void)
447489 {
448490 if (strcmp (var .value , "auto" ) == 0 )
449491 frameskip_type = 1 ;
450- else if (strcmp (var .value , "aggressive " ) == 0 )
492+ else if (strcmp (var .value , "manual " ) == 0 )
451493 frameskip_type = 2 ;
452- else if (strcmp (var .value , "max" ) == 0 )
453- frameskip_type = 3 ;
454494 }
455495
456- if (frameskip_type != prev_frameskip_type )
457- retro_set_audio_buff_status_cb ();
496+ var .key = "catsfc_frameskip_threshold" ;
497+ var .value = NULL ;
498+
499+ frameskip_threshold = 33 ;
500+
501+ if (environ_cb (RETRO_ENVIRONMENT_GET_VARIABLE , & var ) && var .value )
502+ frameskip_threshold = strtol (var .value , NULL , 10 );
458503
459504 var .key = "catsfc_overclock_cycles" ;
460505 var .value = NULL ;
@@ -489,6 +534,13 @@ static void check_variables(void)
489534 else
490535 reduce_sprite_flicker = false;
491536 }
537+
538+ /* Reinitialise frameskipping, if required */
539+ if (!first_run &&
540+ ((frameskip_type != prev_frameskip_type ) ||
541+ (Settings .ForceNTSC != prev_force_ntsc ) ||
542+ (Settings .ForcePAL != prev_force_pal )))
543+ retro_set_audio_buff_status_cb ();
492544}
493545
494546static int32_t samples_to_play = 0 ;
@@ -502,7 +554,7 @@ void retro_run(void)
502554#endif
503555
504556 if (environ_cb (RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE , & updated ) && updated )
505- check_variables ();
557+ check_variables (false );
506558
507559#ifdef NO_VIDEO_OUTPUT
508560 video_cb (NULL , IPPU .RenderedScreenWidth , IPPU .RenderedScreenHeight , GFX .Pitch );
@@ -544,11 +596,8 @@ void retro_run(void)
544596 case 1 : /* auto */
545597 skip_frame = retro_audio_buff_underrun ;
546598 break ;
547- case 2 : /* aggressive */
548- skip_frame = (retro_audio_buff_occupancy < 33 );
549- break ;
550- case 3 : /* max */
551- skip_frame = (retro_audio_buff_occupancy < 50 );
599+ case 2 : /* manual */
600+ skip_frame = (retro_audio_buff_occupancy < frameskip_threshold );
552601 break ;
553602 default :
554603 skip_frame = false;
@@ -567,6 +616,18 @@ void retro_run(void)
567616 }
568617 }
569618
619+ /* If frameskip/timing settings have changed,
620+ * update frontend audio latency
621+ * > Can do this before or after the frameskip
622+ * check, but doing it after means we at least
623+ * retain the current frame's audio output */
624+ if (update_audio_latency )
625+ {
626+ environ_cb (RETRO_ENVIRONMENT_SET_MINIMUM_AUDIO_LATENCY ,
627+ & retro_audio_latency );
628+ update_audio_latency = false;
629+ }
630+
570631 poll_cb ();
571632
572633 RETRO_PERFORMANCE_INIT (S9xMainLoop_func );
@@ -960,7 +1021,7 @@ bool retro_load_game(const struct retro_game_info* game)
9601021
9611022 CPU .Flags = 0 ;
9621023 init_descriptors ();
963- check_variables ();
1024+ check_variables (true );
9641025
9651026#ifdef LOAD_FROM_MEMORY_TEST
9661027 if (!LoadROM (game ))
0 commit comments