diff --git a/libretro-common/include/libretro.h b/libretro-common/include/libretro.h
index e03c5e40c8..8b579edcb4 100644
--- a/libretro-common/include/libretro.h
+++ b/libretro-common/include/libretro.h
@@ -1335,6 +1335,15 @@ enum retro_mod
                                             * should be considered active.
                                             */
 
+#define RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK 62
+                                           /* const struct retro_audio_buffer_status_callback * --
+                                            * Lets the core know the occupancy level of the frontend
+                                            * audio buffer. Can be used by a core to attempt frame
+                                            * skipping in order to avoid buffer under-runs.
+                                            * A core may pass NULL to disable buffer status reporting
+                                            * in the frontend.
+                                            */
+
 /* VFS functionality */
 
 /* File paths:
@@ -2224,6 +2233,30 @@ struct retro_frame_time_callback
    retro_usec_t reference;
 };
 
+/* Notifies a libretro core of the current occupancy
+ * level of the frontend audio buffer.
+ *
+ * - active: 'true' if audio buffer is currently
+ *           in use. Will be 'false' if audio is
+ *           disabled in the frontend
+ *
+ * - occupancy: Given as a value in the range [0,100],
+ *              corresponding to the occupancy percentage
+ *              of the audio buffer
+ *
+ * - underrun_likely: 'true' if the frontend expects an
+ *                    audio buffer underrun during the
+ *                    next frame (indicates that a core
+ *                    should attempt frame skipping)
+ *
+ * It will be called right before retro_run() every frame. */
+typedef void (RETRO_CALLCONV *retro_audio_buffer_status_callback_t)(
+      bool active, unsigned occupancy, bool underrun_likely);
+struct retro_audio_buffer_status_callback
+{
+   retro_audio_buffer_status_callback_t callback;
+};
+
 /* Pass this to retro_video_refresh_t if rendering to hardware.
  * Passing NULL to retro_video_refresh_t is still a frame dupe as normal.
  * */
diff --git a/retroarch.c b/retroarch.c
index c2bbe7899f..95430401af 100644
--- a/retroarch.c
+++ b/retroarch.c
@@ -2156,6 +2156,7 @@ struct rarch_state
    gfx_display_t              dispgfx;                   /* ptr alignment */
    input_keyboard_press_t keyboard_press_cb;             /* ptr alignment */
    struct retro_frame_time_callback runloop_frame_time;  /* ptr alignment */
+   struct retro_audio_buffer_status_callback runloop_audio_buffer_status; /* ptr alignment */
    retro_input_state_t input_state_callback_original;    /* ptr alignment */
    struct retro_audio_callback audio_callback;           /* ptr alignment */
    retro_keyboard_event_t runloop_key_event;             /* ptr alignment */
@@ -19891,6 +19892,21 @@ static bool rarch_environment_cb(unsigned cmd, void *data)
          break;
       }
 
+      case RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK:
+      {
+         const struct retro_audio_buffer_status_callback *info =
+            (const struct retro_audio_buffer_status_callback*)data;
+
+         RARCH_LOG("[Environ]: SET_AUDIO_BUFFER_STATUS_CALLBACK.\n");
+
+         if (info)
+            p_rarch->runloop_audio_buffer_status.callback = info->callback;
+         else
+            p_rarch->runloop_audio_buffer_status.callback = NULL;
+
+         break;
+      }
+
       case RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE:
       {
          struct retro_rumble_interface *iface =
@@ -20880,6 +20896,8 @@ static void uninit_libretro_symbols(
       retroarch_deinit_core_options(p_rarch);
    retroarch_system_info_free(p_rarch);
    retroarch_frame_time_free(p_rarch);
+   memset(&p_rarch->runloop_audio_buffer_status, 0,
+         sizeof(struct retro_audio_buffer_status_callback));
    p_rarch->camera_driver_active      = false;
    p_rarch->location_driver_active    = false;
 
@@ -38049,6 +38067,8 @@ bool rarch_ctl(enum rarch_ctl_state state, void *data)
 #endif
          p_rarch->runloop_autosave         = false;
          retroarch_frame_time_free(p_rarch);
+         memset(&p_rarch->runloop_audio_buffer_status, 0,
+               sizeof(struct retro_audio_buffer_status_callback));
          break;
       case RARCH_CTL_IS_IDLE:
          return p_rarch->runloop_idle;
@@ -40079,6 +40099,45 @@ int runloop_iterate(void)
       p_rarch->runloop_frame_time.callback(delta);
    }
 
+   /* Update audio buffer occupancy if buffer status
+    * callback is in use by the core */
+   if (p_rarch->runloop_audio_buffer_status.callback)
+   {
+      bool audio_buf_active        = false;
+      unsigned audio_buf_occupancy = 0;
+      bool audio_buf_underrun      = false;
+
+      if (!(p_rarch->runloop_paused       ||
+            !p_rarch->audio_driver_active ||
+            !p_rarch->audio_driver_output_samples_buf) &&
+          p_rarch->current_audio->write_avail &&
+          p_rarch->audio_driver_context_audio_data &&
+          p_rarch->audio_driver_buffer_size)
+      {
+         size_t audio_buf_avail = p_rarch->current_audio->write_avail(
+               p_rarch->audio_driver_context_audio_data);
+
+         audio_buf_avail = (audio_buf_avail > p_rarch->audio_driver_buffer_size) ?
+               p_rarch->audio_driver_buffer_size : audio_buf_avail;
+
+         audio_buf_occupancy = (unsigned)(100 - (audio_buf_avail * 100) /
+               p_rarch->audio_driver_buffer_size);
+
+         /* Elsewhere, we standardise on a 'low water mark'
+          * of 25% of the total audio buffer size - use
+          * the same metric here (can be made more sophisticated
+          * if required - i.e. determine buffer occupancy in
+          * terms of usec, and weigh this against the expected
+          * frame time) */
+         audio_buf_underrun  = audio_buf_occupancy < 25;
+
+         audio_buf_active    = true;
+      }
+
+      p_rarch->runloop_audio_buffer_status.callback(
+            audio_buf_active, audio_buf_occupancy, audio_buf_underrun);
+   }
+
    switch ((enum runloop_state)runloop_check_state(p_rarch,
             settings, current_time))
    {