GTK+: Clean up speed throttling.

No more frameskip selection, only 4 options:

* Timer throttling to Settings.FrameRate
* Same, but skips frames when late
* Wait on sound buffer
* Don't throttle.

Dynamic rate control is automatically disabled for option 3.
This commit is contained in:
Brandon Wright 2018-10-28 16:55:33 -05:00
parent 7dcf6a0ce4
commit 79b1ab0250
9 changed files with 168 additions and 294 deletions

View File

@ -204,7 +204,7 @@ else
fi
PKG_CHECK_MODULES([GTK], ["$GTK_VERSION"])
PKG_CHECK_MODULES([GLIB], [gthread-2.0 >= 2.6 gobject-2.0 >= 2.6])
PKG_CHECK_MODULES([GLIB], [glib-2.0 > 2.28 gthread-2.0 >= 2.6 gobject-2.0 >= 2.6])
PKG_CHECK_MODULES([LIBXML], [libxml-2.0 >= 2.0])
PKG_CHECK_MODULES([XRANDR], [xrandr])

View File

@ -413,7 +413,6 @@ Snes9xConfig::save_config_file (void)
xml_out_int (xml, "sound_buffer_size", sound_buffer_size);
xml_out_int (xml, "sound_driver", sound_driver);
xml_out_int (xml, "sound_input_rate", sound_input_rate);
xml_out_int (xml, "sound_sync", Settings.SoundSync);
xml_out_int (xml, "dynamic_rate_control", Settings.DynamicRateControl);
xml_out_int (xml, "dynamic_rate_limit", Settings.DynamicRateLimit);
xml_out_int (xml, "auto_input_rate", auto_input_rate);
@ -653,6 +652,8 @@ Snes9xConfig::set_option (const char *name, const char *value)
else if (!strcasecmp (name, "frameskip"))
{
Settings.SkipFrames = atoi (value);
if (Settings.SkipFrames == THROTTLE_SOUND_SYNC)
Settings.SoundSync = 1;
}
else if (!strcasecmp (name, "sound_emulation"))
{
@ -918,10 +919,6 @@ Snes9xConfig::set_option (const char *name, const char *value)
{
Settings.UpAndDown = CLAMP (atoi (value), 0, 1);
}
else if (!strcasecmp (name, "sound_sync"))
{
Settings.SoundSync = atoi (value) ? 1 : 0;
}
else if (!strcasecmp (name, "rewind_buffer_size"))
{
rewind_buffer_size = CLAMP (atoi (value), 0, 2000);

View File

@ -1,7 +1,6 @@
#ifndef __GTK_CONFIG_H
#define __GTK_CONFIG_H
#include <sys/time.h>
#include <libxml/parser.h>
#include <X11/Xlib.h>
#include <X11/extensions/Xrandr.h>
@ -9,17 +8,30 @@
#include "gtk_control.h"
#include "snes_ntsc.h"
#define HWA_NONE 0
#define HWA_OPENGL 1
#define HWA_XV 2
enum {
HWA_NONE = 0,
HWA_OPENGL = 1,
HWA_XV = 2
};
#define HIRES_MERGE 0
#define HIRES_NORMAL 1
#define HIRES_SCALE 2
enum {
HIRES_MERGE = 0,
HIRES_NORMAL = 1,
HIRES_SCALE = 2
};
#define ESC_TOGGLE_MENUBAR 0
#define ESC_EXIT_FULLSCREEN 1
#define ESC_EXIT_SNES9X 2
enum {
ESC_TOGGLE_MENUBAR = 0,
ESC_EXIT_FULLSCREEN = 1,
ESC_EXIT_SNES9X = 2
};
enum {
THROTTLE_TIMER = 0,
THROTTLE_TIMER_FRAMESKIP = 1,
THROTTLE_SOUND_SYNC = 2,
THROTTLE_NONE = 3
};
class Snes9xConfig
{
@ -128,11 +140,11 @@ class Snes9xConfig
unsigned char screensaver_needs_reset;
int modal_dialogs;
int pointer_is_visible;
struct timeval pointer_timestamp;
unsigned int rewind_granularity;
unsigned int rewind_buffer_size;
int pointer_is_visible;
gint64 pointer_timestamp;
unsigned int rewind_granularity;
unsigned int rewind_buffer_size;
XRRScreenResources *xrr_screen_resources;
XRRCrtcInfo *xrr_crtc_info;

View File

@ -1,6 +1,4 @@
#include <gtk/gtk.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <dlfcn.h>
#include <sys/stat.h>
#include <fcntl.h>

View File

@ -361,6 +361,23 @@ event_hw_accel_changed (GtkComboBox *widget, gpointer data)
return;
}
static void
event_frameskip_combo_changed (GtkComboBox *widget, gpointer user_data)
{
Snes9xPreferences *window = (Snes9xPreferences *) user_data;
if (window->get_combo ("frameskip_combo") == THROTTLE_SOUND_SYNC)
{
window->set_check ("dynamic_rate_control", 0);
window->enable_widget ("dynamic_rate_control", 0);
}
else
{
window->enable_widget ("dynamic_rate_control", 1);
}
}
static void
event_scale_method_changed (GtkComboBox *widget, gpointer user_data)
{
@ -584,6 +601,7 @@ Snes9xPreferences::Snes9xPreferences (Snes9xConfig *config) :
{ "game_data_clear", G_CALLBACK (event_game_data_clear) },
{ "about_clicked", G_CALLBACK (event_about_clicked) },
{ "auto_input_rate_toggled", G_CALLBACK (event_auto_input_rate_toggled) },
{ "frameskip_combo_changed", G_CALLBACK (event_frameskip_combo_changed) },
#ifdef USE_JOYSTICK
{ "calibrate", G_CALLBACK (event_calibrate) },
#endif
@ -711,7 +729,8 @@ Snes9xPreferences::move_settings_to_dialog (void)
config->auto_input_rate ? FALSE : TRUE);
set_spin ("sound_buffer_size", config->sound_buffer_size);
set_check ("sync_sound", Settings.SoundSync);
if (Settings.SkipFrames == THROTTLE_SOUND_SYNC)
Settings.DynamicRateControl = 0;
set_check ("dynamic_rate_control", Settings.DynamicRateControl);
set_spin ("dynamic_rate_limit", Settings.DynamicRateLimit / 1000.0);
set_spin ("rewind_buffer_size", config->rewind_buffer_size);
@ -762,9 +781,8 @@ Snes9xPreferences::move_settings_to_dialog (void)
set_combo ("ntsc_scanline_intensity", config->ntsc_scanline_intensity);
set_combo ("scanline_filter_intensity", config->scanline_filter_intensity);
set_combo ("frameskip_combo",
Settings.SkipFrames == AUTO_FRAMERATE ?
0 : Settings.SkipFrames + 1);
set_combo ("frameskip_combo", Settings.SkipFrames);
enable_widget ("dynamic_rate_control", Settings.SkipFrames != THROTTLE_SOUND_SYNC);
set_check ("bilinear_filter", Settings.BilinearFilter);
#ifdef USE_OPENGL
@ -806,6 +824,11 @@ Snes9xPreferences::get_settings_from_dialog (void)
{
int sound_needs_restart = 0;
int gfx_needs_restart = 0;
int sound_sync = 0;
Settings.SkipFrames = get_combo ("frameskip_combo");
if (Settings.SkipFrames == THROTTLE_SOUND_SYNC)
sound_sync = 1;
if ((config->sound_driver != get_combo ("sound_driver")) ||
(config->mute_sound != get_check ("mute_sound_check")) ||
@ -814,7 +837,7 @@ Snes9xPreferences::get_settings_from_dialog (void)
(config->sound_playback_rate != (7 - (get_combo ("playback_combo")))) ||
(config->sound_input_rate != get_slider ("sound_input_rate")) ||
(config->auto_input_rate != get_check ("auto_input_rate")) ||
(Settings.SoundSync != get_check ("sync_sound")) ||
(Settings.SoundSync != sound_sync) ||
(Settings.DynamicRateControl != get_check ("dynamic_rate_control")))
{
sound_needs_restart = 1;
@ -858,7 +881,6 @@ Snes9xPreferences::get_settings_from_dialog (void)
Settings.AutoSaveDelay = get_entry_value ("save_sram_after_sec");
config->multithreading = get_check ("multithreading");
config->pause_emulation_on_switch = get_check ("pause_emulation_on_switch");
Settings.SkipFrames = get_combo ("frameskip_combo");
Settings.BlockInvalidVRAMAccessMaster = get_check ("block_invalid_vram_access");
Settings.UpAndDown = get_check ("upanddown");
Settings.SuperFXClockMultiplier = get_spin ("superfx_multiplier");
@ -868,7 +890,7 @@ Snes9xPreferences::get_settings_from_dialog (void)
config->sound_buffer_size = get_spin ("sound_buffer_size");
config->sound_input_rate = get_slider ("sound_input_rate");
config->auto_input_rate = get_check ("auto_input_rate");
Settings.SoundSync = get_check ("sync_sound");
Settings.SoundSync = sound_sync;
config->mute_sound = get_check ("mute_sound_check");
config->mute_sound_turbo = get_check ("mute_sound_turbo_check");
Settings.DynamicRateControl = get_check ("dynamic_rate_control");
@ -988,11 +1010,6 @@ Snes9xPreferences::get_settings_from_dialog (void)
strncpy (config->sram_directory, safety_sram_directory, PATH_MAX);
}
if (Settings.SkipFrames == 0)
Settings.SkipFrames = AUTO_FRAMERATE;
else
Settings.SkipFrames--;
memcpy (config->pad, pad, (sizeof (JoypadBinding)) * NUM_JOYPADS);
memcpy (config->shortcut, shortcut, (sizeof (Binding)) * NUM_EMU_LINKS);

View File

@ -1,5 +1,4 @@
#include <stdio.h>
#include <sys/time.h>
#include <signal.h>
#include <gdk/gdk.h>
#ifdef GDK_WINDOWING_X11
@ -17,33 +16,26 @@
#include "gtk_netplay.h"
#endif
#define IDLE_FUNC_PRIORITY (G_PRIORITY_DEFAULT_IDLE)
void S9xPostRomInit (void);
void S9xSyncSpeedFinish (void);
static void S9xCheckPointerTimer (void);
void S9xPostRomInit ();
static void S9xThrottle ();
static void S9xCheckPointerTimer ();
static gboolean S9xIdleFunc (gpointer data);
static gboolean S9xScreenSaverCheckFunc (gpointer data);
Snes9xWindow *top_level;
Snes9xConfig *gui_config;
StateManager stateMan;
static struct timeval next_frame_time = { 0, 0 };
static struct timeval now;
static int needs_fullscreening = FALSE;
int syncing;
guint idle_func_id;
Snes9xWindow *top_level;
Snes9xConfig *gui_config;
StateManager state_manager;
static int needs_fullscreening = FALSE;
guint idle_func_id;
gint64 frame_clock = -1;
gint64 pointer_timestamp = -1;
void
S9xTerm (int signal)
void S9xTerm (int signal)
{
S9xExit ();
return;
}
int
main (int argc, char *argv[])
int main (int argc, char *argv[])
{
struct sigaction sig_callback;
@ -118,8 +110,7 @@ main (int argc, char *argv[])
top_level->update_accels ();
Settings.Paused = TRUE;
syncing = 0;
idle_func_id = g_idle_add_full (IDLE_FUNC_PRIORITY,
idle_func_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
S9xIdleFunc,
NULL,
NULL);
@ -160,8 +151,7 @@ main (int argc, char *argv[])
return 0;
}
int
S9xOpenROM (const char *rom_filename)
int S9xOpenROM (const char *rom_filename)
{
uint32 flags;
bool8 loaded;
@ -237,7 +227,7 @@ S9xOpenROM (const char *rom_filename)
CPU.Flags = flags;
if (stateMan.init (gui_config->rewind_buffer_size * 1024 * 1024))
if (state_manager.init (gui_config->rewind_buffer_size * 1024 * 1024))
{
printf ("Using rewind buffer of %uMB\n", gui_config->rewind_buffer_size);
}
@ -247,8 +237,7 @@ S9xOpenROM (const char *rom_filename)
return 0;
}
void
S9xROMLoaded (void)
void S9xROMLoaded ()
{
gui_config->rom_loaded = TRUE;
top_level->configure_widgets ();
@ -260,40 +249,18 @@ S9xROMLoaded (void)
}
S9xSoundStart ();
return;
}
void
S9xNoROMLoaded (void)
void S9xNoROMLoaded ()
{
S9xSoundStop ();
gui_config->rom_loaded = FALSE;
S9xDisplayRefresh (-1, -1);
top_level->configure_widgets ();
top_level->update_statusbar ();
return;
}
/*
static inline void check_messages (void)
{
static unsigned int current_timeout = 0;
if (GFX.InfoStringTimeout > current_timeout)
{
top_level->show_status_message (GFX.InfoString);
}
current_timeout = GFX.InfoStringTimeout;
return;
}
*/
gboolean
S9xPauseFunc (gpointer data)
gboolean S9xPauseFunc (gpointer data)
{
S9xProcessEvents (TRUE);
@ -325,7 +292,7 @@ S9xPauseFunc (gpointer data)
#endif
/* Resume high-performance callback */
idle_func_id = g_idle_add_full (IDLE_FUNC_PRIORITY,
idle_func_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
S9xIdleFunc,
NULL,
NULL);
@ -336,8 +303,7 @@ S9xPauseFunc (gpointer data)
return TRUE;
}
gboolean
S9xIdleFunc (gpointer data)
gboolean S9xIdleFunc (gpointer data)
{
if (needs_fullscreening)
{
@ -367,11 +333,8 @@ S9xIdleFunc (gpointer data)
return FALSE;
}
if (syncing)
S9xSyncSpeedFinish ();
S9xCheckPointerTimer ();
S9xThrottle ();
S9xProcessEvents (TRUE);
#ifdef NETPLAY_SUPPORT
@ -385,13 +348,13 @@ S9xIdleFunc (gpointer data)
for (int i = 0; i < 8; i++)
joypads[i] = MovieGetJoypad(i);
Settings.Rewinding = stateMan.pop();
Settings.Rewinding = state_manager.pop();
for (int i = 0; i < 8; i++)
MovieSetJoypad (i, joypads[i]);
}
else if(IPPU.TotalEmulatedFrames % gui_config->rewind_granularity == 0)
stateMan.push();
state_manager.push();
static int muted_from_turbo = FALSE;
static int mute_saved_state = FALSE;
@ -419,8 +382,7 @@ S9xIdleFunc (gpointer data)
return TRUE;
}
gboolean
S9xScreenSaverCheckFunc (gpointer data)
gboolean S9xScreenSaverCheckFunc (gpointer data)
{
if (!Settings.Paused &&
@ -432,18 +394,12 @@ S9xScreenSaverCheckFunc (gpointer data)
}
/* Snes9x core hooks */
void
S9xMessage (int type, int number, const char *message)
void S9xMessage (int type, int number, const char *message)
{
/*
fprintf (stderr, "%s\n", message);
*/
return;
}
/* Varies from ParseArgs because this one is for the OS port to handle */
void
S9xParseArg (char **argv, int &i, int argc)
void S9xParseArg (char **argv, int &i, int argc)
{
if (!strcasecmp (argv[i], "-filter"))
{
@ -511,106 +467,33 @@ S9xParseArg (char **argv, int &i, int argc)
{
gui_config->mute_sound = TRUE;
}
return;
}
#undef TIMER_DIFF
#define TIMER_DIFF(a, b) ((((a).tv_sec - (b).tv_sec) * 1000000) + (a).tv_usec - (b).tv_usec)
/* Finishes syncing by using more accurate system sleep functions*/
void
S9xSyncSpeedFinish (void)
static void S9xThrottle ()
{
if (!syncing)
return;
gettimeofday (&now, NULL);
if (Settings.SoundSync && !Settings.DynamicRateControl)
{
while (!S9xSyncSound ())
{
usleep (100);
gettimeofday (&next_frame_time, NULL);
/* If we can't sync sound within a second, we're probably deadlocked */
if (TIMER_DIFF (next_frame_time, now) > 1000000)
{
/* Flush out our sample buffer and give up. */
S9xClearSamples ();
break;
}
}
next_frame_time = now;
return;
}
if (TIMER_DIFF (next_frame_time, now) < -500000)
{
next_frame_time = now;
}
while (timercmp (&next_frame_time, &now, >))
{
int time_left = TIMER_DIFF (next_frame_time, now);
if (time_left > 500000)
{
next_frame_time = now;
break;
}
usleep (time_left);
gettimeofday (&now, NULL);
}
next_frame_time.tv_usec += Settings.FrameTime;
if (next_frame_time.tv_usec >= 1000000)
{
next_frame_time.tv_sec += next_frame_time.tv_usec / 1000000;
next_frame_time.tv_usec %= 1000000;
}
syncing = 0;
return;
}
/* SyncSpeed Handles delays between frames, similar to unix.cpp version,
* cleaned up for clarity, adjusted for GUI event loop */
void
S9xSyncSpeed (void)
{
unsigned int limit;
int lag;
gint64 now;
#ifdef NETPLAY_SUPPORT
if (S9xNetplaySyncSpeed ())
return;
#endif
now = g_get_monotonic_time ();
if (Settings.HighSpeedSeek > 0)
{
Settings.HighSpeedSeek--;
IPPU.RenderThisFrame = FALSE;
IPPU.SkippedFrames = 0;
gettimeofday (&now, NULL);
next_frame_time = now;
syncing = 0;
frame_clock = now;
return;
}
else if (Settings.TurboMode)
if (Settings.TurboMode)
{
if ((++IPPU.FrameSkip >= Settings.TurboSkipFrames)
IPPU.FrameSkip++;
if ((IPPU.FrameSkip >= Settings.TurboSkipFrames)
&& !Settings.HighSpeedSeek)
{
IPPU.FrameSkip = 0;
@ -623,84 +506,93 @@ S9xSyncSpeed (void)
IPPU.RenderThisFrame = FALSE;
}
frame_clock = now;
return;
}
gettimeofday (&now, NULL);
IPPU.RenderThisFrame = TRUE;
if (next_frame_time.tv_sec == 0)
if (now - frame_clock > 500000)
{
next_frame_time = now;
++next_frame_time.tv_usec;
frame_clock = now;
}
if (Settings.SkipFrames == AUTO_FRAMERATE && (!Settings.SoundSync || Settings.DynamicRateControl))
if (Settings.SkipFrames == THROTTLE_SOUND_SYNC &&
!Settings.DynamicRateControl)
{
lag = TIMER_DIFF (now, next_frame_time);
/* We compensate for the frame time by a frame in case it's just a CPU
* discrepancy. We can recover lost time in the next frame anyway. */
if (lag > (int) (Settings.FrameTime))
while (!S9xSyncSound ())
{
if (lag > (int) Settings.FrameTime * 10)
usleep (100);
/* If we can't sync sound within a half-second, we're probably deadlocked */
if (g_get_monotonic_time () - now > 500000)
{
/* Running way too slowly */
next_frame_time = now;
IPPU.RenderThisFrame = 1;
IPPU.SkippedFrames = 0;
S9xClearSamples ();
break;
}
else
}
frame_clock = now;
IPPU.SkippedFrames = 0;
return;
}
else if (Settings.SkipFrames == THROTTLE_NONE)
{
frame_clock = now;
}
else // THROTTLE_TIMER or THROTTLE_TIMER_FRAMESKIP
{
if (Settings.SkipFrames == THROTTLE_TIMER_FRAMESKIP)
{
if (now - frame_clock > Settings.FrameTime)
{
IPPU.RenderThisFrame = 0;
IPPU.SkippedFrames++;
if (IPPU.SkippedFrames < 8)
{
IPPU.RenderThisFrame = FALSE;
frame_clock += Settings.FrameTime;
return;
}
else
{
frame_clock = now - Settings.FrameTime;
}
}
}
else
while (now - frame_clock < Settings.FrameTime)
{
IPPU.RenderThisFrame = 1;
IPPU.SkippedFrames = 0;
usleep (100);
now = g_get_monotonic_time ();
}
frame_clock += Settings.FrameTime;
IPPU.FrameSkip = 0;
IPPU.SkippedFrames = 0;
}
else
{
limit = (Settings.SoundSync && !Settings.DynamicRateControl) ? 1 : Settings.SkipFrames + 1;
IPPU.SkippedFrames++;
IPPU.RenderThisFrame = 0;
if (IPPU.SkippedFrames >= limit)
{
IPPU.RenderThisFrame = 1;
IPPU.SkippedFrames = 0;
}
}
syncing = 1;
return;
}
static void
S9xCheckPointerTimer (void)
void S9xSyncSpeed ()
{
}
static void S9xCheckPointerTimer ()
{
if (!gui_config->pointer_is_visible)
return;
gettimeofday (&now, NULL);
if (TIMER_DIFF (now, gui_config->pointer_timestamp) > 1000000)
if (g_get_monotonic_time () - gui_config->pointer_timestamp > 1000000)
{
top_level->hide_mouse_cursor ();
gui_config->pointer_is_visible = FALSE;
}
return;
}
/* Final exit point, issues exit (0) */
void
S9xExit (void)
void S9xExit ()
{
gui_config->save_config_file ();
@ -726,8 +618,6 @@ S9xExit (void)
delete gui_config;
exit (0);
return;
}
void
@ -808,18 +698,14 @@ S9xPostRomInit (void)
case 0x0A: break; //Barcode Battler
}
}
return;
}
const char *
S9xStringInput(const char *message)
const char *S9xStringInput(const char *message)
{
return NULL;
}
void
S9xExtraUsage (void)
void S9xExtraUsage ()
{
printf ("GTK port options:\n"
"-filter [option] Use a filter to scale the image.\n"
@ -827,5 +713,4 @@ S9xExtraUsage (void)
" super2xsai hq2x hq3x hq4x 2xbrz 3xbrz 4xbrz epx ntsc\n"
"\n"
"-mutesound Disables sound output.\n");
return;
}

View File

@ -30,8 +30,8 @@
#define bind_textdomain_codeset(Domain,Codeset) (Codeset)
#endif /* ENABLE_NLS */
#define SNES9X_GTK_AUTHORS "(c) 2007 - 2017 Brandon Wright (bearoso@gmail.com)"
#define SNES9X_GTK_VERSION "85"
#define SNES9X_GTK_AUTHORS "(c) 2007 - 2018 Brandon Wright (bearoso@gmail.com)"
#define SNES9X_GTK_VERSION "86"
extern Snes9xWindow *top_level;
extern Snes9xConfig *gui_config;
@ -46,7 +46,7 @@ extern Snes9xConfig *gui_config;
#endif
int S9xOpenROM (const char *filename);
void S9xNoROMLoaded (void);
void S9xROMLoaded (void);
void S9xNoROMLoaded ();
void S9xROMLoaded ();
#endif /* __GTK_S9X_H */

View File

@ -259,7 +259,7 @@ event_motion_notify (GtkWidget *widget,
window->show_mouse_cursor ();
}
gettimeofday (&(window->config->pointer_timestamp), NULL);
window->config->pointer_timestamp = g_get_monotonic_time ();
return FALSE;
}

View File

@ -1052,37 +1052,16 @@
</columns>
<data>
<row>
<col id="0" translatable="yes">Automatic</col>
<col id="0" translatable="yes">Timer-based</col>
</row>
<row>
<col id="0" translatable="yes">0</col>
<col id="0" translatable="yes">Timer-based with automatic frame-skipping</col>
</row>
<row>
<col id="0" translatable="yes">1</col>
<col id="0" translatable="yes">Sound buffer synchronization</col>
</row>
<row>
<col id="0" translatable="yes">2</col>
</row>
<row>
<col id="0" translatable="yes">3</col>
</row>
<row>
<col id="0" translatable="yes">4</col>
</row>
<row>
<col id="0" translatable="yes">5</col>
</row>
<row>
<col id="0" translatable="yes">6</col>
</row>
<row>
<col id="0" translatable="yes">7</col>
</row>
<row>
<col id="0" translatable="yes">8</col>
</row>
<row>
<col id="0" translatable="yes">9</col>
<col id="0" translatable="yes">No throttling, but can use vsync to control speed</col>
</row>
</data>
</object>
@ -4135,21 +4114,6 @@
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="sync_sound">
<property name="label" translatable="yes">Synchronize with sound</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Base emulation speed on the rate sound is output</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="auto_input_rate">
<property name="label" translatable="yes">Automatically adjust input rate to display</property>
@ -4163,7 +4127,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
<property name="position">1</property>
</packing>
</child>
<child>
@ -4172,13 +4136,13 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Smoothes out slight hiccups in sound input rate</property>
<property name="tooltip_text" translatable="yes">Smoothes out slight hiccups in sound input rate (can't be used with sound buffer synchronization)</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
<property name="position">2</property>
</packing>
</child>
@ -4195,7 +4159,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">4</property>
<property name="position">3</property>
</packing>
</child>
<child>
@ -4211,7 +4175,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">5</property>
<property name="position">4</property>
</packing>
</child>
<child>
@ -4227,7 +4191,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">6</property>
<property name="position">5</property>
</packing>
</child>
<child>
@ -4447,7 +4411,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">7</property>
<property name="position">6</property>
</packing>
</child>
</object>
@ -4550,7 +4514,7 @@
<object class="GtkLabel" id="label45">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Frameskip:</property>
<property name="label" translatable="yes">Speed throttle:</property>
</object>
<packing>
<property name="expand">False</property>
@ -4563,6 +4527,7 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="model">liststore5</property>
<signal name="changed" handler="frameskip_combo_changed" swapped="no"/>
<child>
<object class="GtkCellRendererText" id="cellrenderertext5"/>
<attributes>