mirror of https://github.com/snes9xgit/snes9x.git
Merge remote-tracking branch 'origin/ratecontrol'
This commit is contained in:
commit
f5106af053
24
apu/apu.cpp
24
apu/apu.cpp
|
@ -199,7 +199,7 @@
|
||||||
|
|
||||||
#include "snes/snes.hpp"
|
#include "snes/snes.hpp"
|
||||||
|
|
||||||
#define APU_DEFAULT_INPUT_RATE 32000
|
#define APU_DEFAULT_INPUT_RATE 31987 // 60hz
|
||||||
#define APU_MINIMUM_SAMPLE_COUNT 512
|
#define APU_MINIMUM_SAMPLE_COUNT 512
|
||||||
#define APU_MINIMUM_SAMPLE_BLOCK 128
|
#define APU_MINIMUM_SAMPLE_BLOCK 128
|
||||||
#define APU_NUMERATOR_NTSC 15664
|
#define APU_NUMERATOR_NTSC 15664
|
||||||
|
@ -240,6 +240,8 @@ namespace spc
|
||||||
if necessary on game load. */
|
if necessary on game load. */
|
||||||
static uint32 ratio_numerator = APU_NUMERATOR_NTSC;
|
static uint32 ratio_numerator = APU_NUMERATOR_NTSC;
|
||||||
static uint32 ratio_denominator = APU_DENOMINATOR_NTSC;
|
static uint32 ratio_denominator = APU_DENOMINATOR_NTSC;
|
||||||
|
|
||||||
|
static double dynamic_rate_multiplier = 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace msu
|
namespace msu
|
||||||
|
@ -274,7 +276,7 @@ static void DeStereo (uint8 *buffer, int sample_count)
|
||||||
int16 *buf = (int16 *) buffer;
|
int16 *buf = (int16 *) buffer;
|
||||||
int32 s1, s2;
|
int32 s1, s2;
|
||||||
|
|
||||||
for (int i = 0; i < sample_count >> 1; i++)
|
for (int i = 0; i < (sample_count >> 1); i++)
|
||||||
{
|
{
|
||||||
s1 = (int32) buf[2 * i];
|
s1 = (int32) buf[2 * i];
|
||||||
s2 = (int32) buf[2 * i + 1];
|
s2 = (int32) buf[2 * i + 1];
|
||||||
|
@ -348,7 +350,7 @@ bool8 S9xMixSamples (uint8 *buffer, int sample_count)
|
||||||
if (msu::resampler->avail() >= sample_count)
|
if (msu::resampler->avail() >= sample_count)
|
||||||
{
|
{
|
||||||
msu::resampler->read((short *)msu::resample_buffer, sample_count);
|
msu::resampler->read((short *)msu::resample_buffer, sample_count);
|
||||||
for (uint32 i = 0; i < sample_count; ++i)
|
for (int i = 0; i < sample_count; ++i)
|
||||||
*((int16*)(dest+(i * 2))) += *((int16*)(msu::resample_buffer +(i * 2)));
|
*((int16*)(dest+(i * 2))) += *((int16*)(msu::resample_buffer +(i * 2)));
|
||||||
}
|
}
|
||||||
else // should never occur
|
else // should never occur
|
||||||
|
@ -471,12 +473,28 @@ void S9xSetSamplesAvailableCallback (apu_callback callback, void *data)
|
||||||
spc::extra_data = data;
|
spc::extra_data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void S9xUpdateDynamicRate (int avail, int buffer_size)
|
||||||
|
{
|
||||||
|
int half_size = buffer_size / 2;
|
||||||
|
int delta_mid = avail - half_size;
|
||||||
|
double direction = (double) delta_mid / half_size;
|
||||||
|
spc::dynamic_rate_multiplier = 1.0 - (Settings.DynamicRateLimit / 100000.0) * direction;
|
||||||
|
|
||||||
|
UpdatePlaybackRate();
|
||||||
|
}
|
||||||
|
|
||||||
static void UpdatePlaybackRate (void)
|
static void UpdatePlaybackRate (void)
|
||||||
{
|
{
|
||||||
if (Settings.SoundInputRate == 0)
|
if (Settings.SoundInputRate == 0)
|
||||||
Settings.SoundInputRate = APU_DEFAULT_INPUT_RATE;
|
Settings.SoundInputRate = APU_DEFAULT_INPUT_RATE;
|
||||||
|
|
||||||
double time_ratio = (double) Settings.SoundInputRate * spc::timing_hack_numerator / (Settings.SoundPlaybackRate * spc::timing_hack_denominator);
|
double time_ratio = (double) Settings.SoundInputRate * spc::timing_hack_numerator / (Settings.SoundPlaybackRate * spc::timing_hack_denominator);
|
||||||
|
|
||||||
|
if (Settings.DynamicRateControl)
|
||||||
|
{
|
||||||
|
time_ratio *= spc::dynamic_rate_multiplier;
|
||||||
|
}
|
||||||
|
|
||||||
spc::resampler->time_ratio(time_ratio);
|
spc::resampler->time_ratio(time_ratio);
|
||||||
|
|
||||||
if (Settings.MSU1)
|
if (Settings.MSU1)
|
||||||
|
|
|
@ -229,5 +229,6 @@ void S9xFinalizeSamples (void);
|
||||||
void S9xClearSamples (void);
|
void S9xClearSamples (void);
|
||||||
bool8 S9xMixSamples (uint8 *, int);
|
bool8 S9xMixSamples (uint8 *, int);
|
||||||
void S9xSetSamplesAvailableCallback (apu_callback, void *);
|
void S9xSetSamplesAvailableCallback (apu_callback, void *);
|
||||||
|
void S9xUpdateDynamicRate (int, int);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -52,7 +52,6 @@ class HermiteResampler : public Resampler
|
||||||
time_ratio (double ratio)
|
time_ratio (double ratio)
|
||||||
{
|
{
|
||||||
r_step = ratio;
|
r_step = ratio;
|
||||||
clear ();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -83,7 +82,7 @@ class HermiteResampler : public Resampler
|
||||||
while (r_frac <= 1.0 && o_position < num_samples)
|
while (r_frac <= 1.0 && o_position < num_samples)
|
||||||
{
|
{
|
||||||
hermite_val[0] = hermite (r_frac, r_left [0], r_left [1], r_left [2], r_left [3]);
|
hermite_val[0] = hermite (r_frac, r_left [0], r_left [1], r_left [2], r_left [3]);
|
||||||
hermite_val[1] = hermite (r_frac, r_right[0], r_right[1], r_right[2], r_right[3]);
|
hermite_val[1] = hermite (r_frac, r_right[0], r_right[1], r_right[2], r_right[3]);
|
||||||
data[o_position] = SHORT_CLAMP (hermite_val[0]);
|
data[o_position] = SHORT_CLAMP (hermite_val[0]);
|
||||||
data[o_position + 1] = SHORT_CLAMP (hermite_val[1]);
|
data[o_position + 1] = SHORT_CLAMP (hermite_val[1]);
|
||||||
|
|
||||||
|
|
|
@ -226,7 +226,7 @@ GtkBuilderWindow::set_combo (const char *name, unsigned char value)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
GtkBuilderWindow::set_spin (const char *name, unsigned int value)
|
GtkBuilderWindow::set_spin (const char *name, double value)
|
||||||
{
|
{
|
||||||
gtk_spin_button_set_value (GTK_SPIN_BUTTON (get_widget (name)),
|
gtk_spin_button_set_value (GTK_SPIN_BUTTON (get_widget (name)),
|
||||||
(double) value);
|
(double) value);
|
||||||
|
@ -260,11 +260,10 @@ GtkBuilderWindow::get_window (void)
|
||||||
return GTK_WINDOW (window);
|
return GTK_WINDOW (window);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int
|
double
|
||||||
GtkBuilderWindow::get_spin (const char *name)
|
GtkBuilderWindow::get_spin (const char *name)
|
||||||
{
|
{
|
||||||
return (unsigned int)
|
return gtk_spin_button_get_value (GTK_SPIN_BUTTON (get_widget (name)));
|
||||||
gtk_spin_button_get_value (GTK_SPIN_BUTTON (get_widget (name)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|
|
@ -30,13 +30,13 @@ class GtkBuilderWindow
|
||||||
unsigned char get_combo (const char *name);
|
unsigned char get_combo (const char *name);
|
||||||
void combo_box_append (const char *name, const char *value);
|
void combo_box_append (const char *name, const char *value);
|
||||||
void combo_box_append (GtkComboBox *combo, const char *value);
|
void combo_box_append (GtkComboBox *combo, const char *value);
|
||||||
unsigned int get_spin (const char *name);
|
double get_spin (const char *name);
|
||||||
float get_slider (const char *name);
|
float get_slider (const char *name);
|
||||||
void set_check (const char *name, unsigned char value);
|
void set_check (const char *name, unsigned char value);
|
||||||
void set_entry_value (const char *name, unsigned int value);
|
void set_entry_value (const char *name, unsigned int value);
|
||||||
void set_entry_text (const char *name, const char *text);
|
void set_entry_text (const char *name, const char *text);
|
||||||
void set_combo (const char *name, unsigned char value);
|
void set_combo (const char *name, unsigned char value);
|
||||||
void set_spin (const char *name, unsigned int value);
|
void set_spin (const char *name, double value);
|
||||||
void set_slider (const char *name, float value);
|
void set_slider (const char *name, float value);
|
||||||
int has_focus (const char *widget);
|
int has_focus (const char *widget);
|
||||||
|
|
||||||
|
|
|
@ -253,6 +253,8 @@ Snes9xConfig::load_defaults (void)
|
||||||
Settings.FrameTime = Settings.FrameTimeNTSC;
|
Settings.FrameTime = Settings.FrameTimeNTSC;
|
||||||
Settings.BlockInvalidVRAMAccessMaster = TRUE;
|
Settings.BlockInvalidVRAMAccessMaster = TRUE;
|
||||||
Settings.SoundSync = 1;
|
Settings.SoundSync = 1;
|
||||||
|
Settings.DynamicRateControl = 0;
|
||||||
|
Settings.DynamicRateLimit = 1000;
|
||||||
Settings.HDMATimingHack = 100;
|
Settings.HDMATimingHack = 100;
|
||||||
Settings.ApplyCheats = 1;
|
Settings.ApplyCheats = 1;
|
||||||
|
|
||||||
|
@ -402,6 +404,8 @@ Snes9xConfig::save_config_file (void)
|
||||||
xml_out_int (xml, "sound_driver", sound_driver);
|
xml_out_int (xml, "sound_driver", sound_driver);
|
||||||
xml_out_int (xml, "sound_input_rate", sound_input_rate);
|
xml_out_int (xml, "sound_input_rate", sound_input_rate);
|
||||||
xml_out_int (xml, "sound_sync", Settings.SoundSync);
|
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);
|
||||||
|
|
||||||
/* Snes9X core-stored variables */
|
/* Snes9X core-stored variables */
|
||||||
xml_out_int (xml, "transparency", Settings.Transparency);
|
xml_out_int (xml, "transparency", Settings.Transparency);
|
||||||
|
@ -664,6 +668,14 @@ Snes9xConfig::set_option (const char *name, const char *value)
|
||||||
{
|
{
|
||||||
Settings.ReverseStereo = atoi (value);
|
Settings.ReverseStereo = atoi (value);
|
||||||
}
|
}
|
||||||
|
else if (!strcasecmp (name, "dynamic_rate_control"))
|
||||||
|
{
|
||||||
|
Settings.DynamicRateControl = atoi (value);
|
||||||
|
}
|
||||||
|
else if (!strcasecmp (name, "dynamic_rate_limit"))
|
||||||
|
{
|
||||||
|
Settings.DynamicRateLimit = atoi (value);
|
||||||
|
}
|
||||||
else if (!strcasecmp (name, "gaussian_interpolation"))
|
else if (!strcasecmp (name, "gaussian_interpolation"))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -661,6 +661,8 @@ Snes9xPreferences::move_settings_to_dialog (void)
|
||||||
set_spin ("sound_buffer_size", config->sound_buffer_size);
|
set_spin ("sound_buffer_size", config->sound_buffer_size);
|
||||||
set_slider ("sound_input_rate", config->sound_input_rate);
|
set_slider ("sound_input_rate", config->sound_input_rate);
|
||||||
set_check ("sync_sound", Settings.SoundSync);
|
set_check ("sync_sound", Settings.SoundSync);
|
||||||
|
set_check ("dynamic_rate_control", Settings.DynamicRateControl);
|
||||||
|
set_spin ("dynamic_rate_limit", Settings.DynamicRateLimit / 100000.0);
|
||||||
set_spin ("rewind_buffer_size", config->rewind_buffer_size);
|
set_spin ("rewind_buffer_size", config->rewind_buffer_size);
|
||||||
set_spin ("rewind_granularity", config->rewind_granularity);
|
set_spin ("rewind_granularity", config->rewind_granularity);
|
||||||
|
|
||||||
|
@ -816,6 +818,8 @@ Snes9xPreferences::get_settings_from_dialog (void)
|
||||||
Settings.SoundSync = get_check ("sync_sound");
|
Settings.SoundSync = get_check ("sync_sound");
|
||||||
config->mute_sound = get_check ("mute_sound_check");
|
config->mute_sound = get_check ("mute_sound_check");
|
||||||
config->mute_sound_turbo = get_check ("mute_sound_turbo_check");
|
config->mute_sound_turbo = get_check ("mute_sound_turbo_check");
|
||||||
|
Settings.DynamicRateControl = get_check ("dynamic_rate_control");
|
||||||
|
Settings.DynamicRateLimit = (uint32) (get_spin ("dynamic_rate_limit") * 100000);
|
||||||
|
|
||||||
store_ntsc_settings ();
|
store_ntsc_settings ();
|
||||||
config->ntsc_scanline_intensity = get_combo ("ntsc_scanline_intensity");
|
config->ntsc_scanline_intensity = get_combo ("ntsc_scanline_intensity");
|
||||||
|
|
|
@ -65,7 +65,10 @@ bool8
|
||||||
S9xAlsaSoundDriver::open_device (void)
|
S9xAlsaSoundDriver::open_device (void)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
unsigned int periods = 8;
|
||||||
|
unsigned int buffer_size = gui_config->sound_buffer_size * 1000;
|
||||||
snd_pcm_sw_params_t *sw_params;
|
snd_pcm_sw_params_t *sw_params;
|
||||||
|
snd_pcm_hw_params_t *hw_params;
|
||||||
snd_pcm_uframes_t alsa_buffer_size, alsa_period_size;
|
snd_pcm_uframes_t alsa_buffer_size, alsa_period_size;
|
||||||
|
|
||||||
printf ("ALSA sound driver initializing...\n");
|
printf ("ALSA sound driver initializing...\n");
|
||||||
|
@ -89,28 +92,28 @@ S9xAlsaSoundDriver::open_device (void)
|
||||||
Settings.SoundPlaybackRate,
|
Settings.SoundPlaybackRate,
|
||||||
gui_config->sound_buffer_size);
|
gui_config->sound_buffer_size);
|
||||||
|
|
||||||
if ((err = snd_pcm_set_params (pcm,
|
snd_pcm_hw_params_alloca (&hw_params);
|
||||||
Settings.SixteenBitSound ? SND_PCM_FORMAT_S16 : SND_PCM_FORMAT_U8,
|
snd_pcm_hw_params_any (pcm, hw_params);
|
||||||
SND_PCM_ACCESS_RW_INTERLEAVED,
|
snd_pcm_hw_params_set_format (pcm, hw_params, Settings.SixteenBitSound ? SND_PCM_FORMAT_S16 : SND_PCM_FORMAT_U8);
|
||||||
Settings.Stereo ? 2 : 1,
|
snd_pcm_hw_params_set_access (pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||||
Settings.SoundPlaybackRate,
|
snd_pcm_hw_params_set_channels (pcm, hw_params, Settings.Stereo ? 2 : 1);
|
||||||
1 /* Allow software resampling */,
|
snd_pcm_hw_params_set_rate_near (pcm, hw_params, &Settings.SoundPlaybackRate, NULL);
|
||||||
gui_config->sound_buffer_size * 1000))
|
snd_pcm_hw_params_set_rate_resample (pcm, hw_params, 0);
|
||||||
< 0)
|
snd_pcm_hw_params_set_buffer_time_near (pcm, hw_params, &buffer_size, NULL);
|
||||||
{
|
snd_pcm_hw_params_set_periods_near (pcm, hw_params, &periods, NULL);
|
||||||
|
|
||||||
|
if ((err = snd_pcm_hw_params (pcm, hw_params)) < 0)
|
||||||
goto close_fail;
|
goto close_fail;
|
||||||
}
|
|
||||||
|
|
||||||
snd_pcm_sw_params_alloca (&sw_params);
|
snd_pcm_sw_params_alloca (&sw_params);
|
||||||
snd_pcm_sw_params_current (pcm, sw_params);
|
snd_pcm_sw_params_current (pcm, sw_params);
|
||||||
snd_pcm_get_params (pcm, &alsa_buffer_size, &alsa_period_size);
|
snd_pcm_get_params (pcm, &alsa_buffer_size, &alsa_period_size);
|
||||||
/* Don't start until we're [nearly] full */
|
/* Don't start until we're [nearly] full */
|
||||||
snd_pcm_sw_params_set_start_threshold (pcm,
|
snd_pcm_sw_params_set_start_threshold (pcm, sw_params, (alsa_buffer_size / 2));
|
||||||
sw_params,
|
|
||||||
(alsa_buffer_size / alsa_period_size) * alsa_period_size);
|
|
||||||
/* Transfer in blocks of period-size */
|
|
||||||
snd_pcm_sw_params_set_avail_min (pcm, sw_params, alsa_period_size);
|
|
||||||
err = snd_pcm_sw_params (pcm, sw_params);
|
err = snd_pcm_sw_params (pcm, sw_params);
|
||||||
|
|
||||||
|
output_buffer_size = snd_pcm_frames_to_bytes (pcm, alsa_buffer_size);
|
||||||
|
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto close_fail;
|
goto close_fail;
|
||||||
|
|
||||||
|
@ -143,17 +146,37 @@ S9xAlsaSoundDriver::samples_available (void)
|
||||||
snd_pcm_sframes_t frames_written, frames;
|
snd_pcm_sframes_t frames_written, frames;
|
||||||
int bytes;
|
int bytes;
|
||||||
|
|
||||||
S9xFinalizeSamples ();
|
frames = snd_pcm_avail (pcm);
|
||||||
|
|
||||||
|
if (Settings.DynamicRateControl)
|
||||||
|
{
|
||||||
|
S9xUpdateDynamicRate (snd_pcm_frames_to_bytes (pcm, frames),
|
||||||
|
output_buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
frames = snd_pcm_avail_update (pcm);
|
|
||||||
if (frames < 0)
|
if (frames < 0)
|
||||||
{
|
{
|
||||||
frames = snd_pcm_recover (pcm, frames, 1);
|
frames = snd_pcm_recover (pcm, frames, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
S9xFinalizeSamples ();
|
||||||
|
|
||||||
|
if (Settings.DynamicRateControl)
|
||||||
|
{
|
||||||
|
// Using rate control, we should always keep the emulator's sound buffers empty to
|
||||||
|
// maintain an accurate measurement.
|
||||||
|
if (frames < (S9xGetSampleCount () >> (Settings.Stereo ? 1 : 0)))
|
||||||
|
{
|
||||||
|
S9xClearSamples ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
frames = MIN (frames, S9xGetSampleCount () >> (Settings.Stereo ? 1 : 0));
|
frames = MIN (frames, S9xGetSampleCount () >> (Settings.Stereo ? 1 : 0));
|
||||||
|
|
||||||
bytes = snd_pcm_frames_to_bytes (pcm, frames);
|
bytes = snd_pcm_frames_to_bytes (pcm, frames);
|
||||||
|
|
||||||
if (bytes <= 0)
|
if (bytes <= 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -21,6 +21,7 @@ class S9xAlsaSoundDriver : public S9xSoundDriver
|
||||||
snd_pcm_t *pcm;
|
snd_pcm_t *pcm;
|
||||||
int sound_buffer_size;
|
int sound_buffer_size;
|
||||||
uint8 *sound_buffer;
|
uint8 *sound_buffer;
|
||||||
|
int output_buffer_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* __GTK_SOUND_DRIVER_ALSA_H */
|
#endif /* __GTK_SOUND_DRIVER_ALSA_H */
|
||||||
|
|
|
@ -64,7 +64,7 @@ bool8
|
||||||
S9xOSSSoundDriver::open_device (void)
|
S9xOSSSoundDriver::open_device (void)
|
||||||
{
|
{
|
||||||
int temp;
|
int temp;
|
||||||
int output_buffer_size;
|
audio_buf_info info;
|
||||||
|
|
||||||
output_buffer_size = (gui_config->sound_buffer_size * Settings.SoundPlaybackRate) / 1000;
|
output_buffer_size = (gui_config->sound_buffer_size * Settings.SoundPlaybackRate) / 1000;
|
||||||
|
|
||||||
|
@ -72,8 +72,6 @@ S9xOSSSoundDriver::open_device (void)
|
||||||
output_buffer_size *= 2;
|
output_buffer_size *= 2;
|
||||||
if (Settings.SixteenBitSound)
|
if (Settings.SixteenBitSound)
|
||||||
output_buffer_size *= 2;
|
output_buffer_size *= 2;
|
||||||
if (output_buffer_size > 65536)
|
|
||||||
output_buffer_size = 65536;
|
|
||||||
if (output_buffer_size < 256)
|
if (output_buffer_size < 256)
|
||||||
output_buffer_size = 256;
|
output_buffer_size = 256;
|
||||||
|
|
||||||
|
@ -84,7 +82,13 @@ S9xOSSSoundDriver::open_device (void)
|
||||||
filedes = open ("/dev/dsp", O_WRONLY | O_NONBLOCK);
|
filedes = open ("/dev/dsp", O_WRONLY | O_NONBLOCK);
|
||||||
|
|
||||||
if (filedes < 0)
|
if (filedes < 0)
|
||||||
goto fail;
|
{
|
||||||
|
printf ("Failed\n --> (Device: /dev/dsp1)...");
|
||||||
|
filedes = open ("/dev/dsp1", O_WRONLY | O_NONBLOCK);
|
||||||
|
|
||||||
|
if (filedes < 0)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
printf ("OK\n");
|
printf ("OK\n");
|
||||||
|
|
||||||
|
@ -132,9 +136,14 @@ S9xOSSSoundDriver::open_device (void)
|
||||||
|
|
||||||
/* OSS requires a power-of-two buffer size, first 16 bits are the number
|
/* OSS requires a power-of-two buffer size, first 16 bits are the number
|
||||||
* of fragments to generate, second 16 are the respective power-of-two. */
|
* of fragments to generate, second 16 are the respective power-of-two. */
|
||||||
temp = (2 << 16) | (S9xSoundBase2log (output_buffer_size));
|
temp = (8 << 16) | (S9xSoundBase2log (output_buffer_size / 8));
|
||||||
|
|
||||||
output_buffer_size = S9xSoundPowerof2 (temp & 0xffff);
|
if (ioctl (filedes, SNDCTL_DSP_SETFRAGMENT, &temp) < 0)
|
||||||
|
goto close_fail;
|
||||||
|
|
||||||
|
ioctl (filedes, SNDCTL_DSP_GETOSPACE, &info);
|
||||||
|
|
||||||
|
output_buffer_size = info.fragsize * info.fragstotal;
|
||||||
|
|
||||||
printf (" --> (Buffer size: %d bytes, %dms latency)...",
|
printf (" --> (Buffer size: %d bytes, %dms latency)...",
|
||||||
output_buffer_size,
|
output_buffer_size,
|
||||||
|
@ -142,9 +151,6 @@ S9xOSSSoundDriver::open_device (void)
|
||||||
>> (Settings.SixteenBitSound ? 1 : 0))
|
>> (Settings.SixteenBitSound ? 1 : 0))
|
||||||
/ (Settings.SoundPlaybackRate));
|
/ (Settings.SoundPlaybackRate));
|
||||||
|
|
||||||
if (ioctl (filedes, SNDCTL_DSP_SETFRAGMENT, &temp) < 0)
|
|
||||||
goto close_fail;
|
|
||||||
|
|
||||||
printf ("OK\n");
|
printf ("OK\n");
|
||||||
|
|
||||||
S9xSetSamplesAvailableCallback (oss_samples_available, this);
|
S9xSetSamplesAvailableCallback (oss_samples_available, this);
|
||||||
|
@ -175,11 +181,27 @@ S9xOSSSoundDriver::samples_available (void)
|
||||||
int bytes_to_write;
|
int bytes_to_write;
|
||||||
int bytes_written;
|
int bytes_written;
|
||||||
|
|
||||||
|
ioctl (filedes, SNDCTL_DSP_GETOSPACE, &info);
|
||||||
|
|
||||||
|
if (Settings.DynamicRateControl)
|
||||||
|
{
|
||||||
|
S9xUpdateDynamicRate (info.bytes, output_buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
S9xFinalizeSamples ();
|
S9xFinalizeSamples ();
|
||||||
|
|
||||||
samples_to_write = S9xGetSampleCount ();
|
samples_to_write = S9xGetSampleCount ();
|
||||||
|
|
||||||
ioctl (filedes, SNDCTL_DSP_GETOSPACE, &info);
|
if (Settings.DynamicRateControl)
|
||||||
|
{
|
||||||
|
// Using rate control, we should always keep the emulator's sound buffers empty to
|
||||||
|
// maintain an accurate measurement.
|
||||||
|
if (samples_to_write > (info.bytes >> (Settings.SixteenBitSound ? 1 : 0)))
|
||||||
|
{
|
||||||
|
S9xClearSamples ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
samples_to_write = MIN (info.bytes >> (Settings.SixteenBitSound ? 1 : 0),
|
samples_to_write = MIN (info.bytes >> (Settings.SixteenBitSound ? 1 : 0),
|
||||||
samples_to_write);
|
samples_to_write);
|
||||||
|
|
|
@ -20,6 +20,7 @@ class S9xOSSSoundDriver : public S9xSoundDriver
|
||||||
int filedes;
|
int filedes;
|
||||||
uint8 *sound_buffer;
|
uint8 *sound_buffer;
|
||||||
int sound_buffer_size;
|
int sound_buffer_size;
|
||||||
|
int output_buffer_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,10 @@
|
||||||
#include "gtk_s9x.h"
|
#include "gtk_s9x.h"
|
||||||
#include "gtk_sound_driver_portaudio.h"
|
#include "gtk_sound_driver_portaudio.h"
|
||||||
|
|
||||||
static int
|
static inline int
|
||||||
port_audio_callback (const void *input,
|
frames_to_bytes (int frames)
|
||||||
void *output,
|
|
||||||
unsigned long frameCount,
|
|
||||||
const PaStreamCallbackTimeInfo* timeInfo,
|
|
||||||
PaStreamCallbackFlags statusFlags,
|
|
||||||
void *userData)
|
|
||||||
{
|
{
|
||||||
((S9xPortAudioSoundDriver *) userData)->mix ((unsigned char *) output, frameCount * (Settings.Stereo ? 2 : 1) * (Settings.SixteenBitSound ? 2 : 1));
|
return (frames * (Settings.SixteenBitSound ? 2 : 1) * (Settings.Stereo ? 2 : 1));
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -25,19 +18,14 @@ port_audio_samples_available_callback (void *data)
|
||||||
void
|
void
|
||||||
S9xPortAudioSoundDriver::mix (unsigned char *output, int bytes)
|
S9xPortAudioSoundDriver::mix (unsigned char *output, int bytes)
|
||||||
{
|
{
|
||||||
g_mutex_lock (mutex);
|
|
||||||
|
|
||||||
S9xMixSamples (output, bytes >> (Settings.SixteenBitSound ? 1 : 0));
|
|
||||||
|
|
||||||
g_mutex_unlock (mutex);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
S9xPortAudioSoundDriver::S9xPortAudioSoundDriver(void)
|
S9xPortAudioSoundDriver::S9xPortAudioSoundDriver(void)
|
||||||
{
|
{
|
||||||
audio_stream = NULL;
|
audio_stream = NULL;
|
||||||
mutex = NULL;
|
sound_buffer = NULL;
|
||||||
|
sound_buffer_size = 0;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -62,16 +50,16 @@ S9xPortAudioSoundDriver::terminate (void)
|
||||||
{
|
{
|
||||||
stop ();
|
stop ();
|
||||||
|
|
||||||
if (mutex)
|
|
||||||
{
|
|
||||||
g_mutex_free (mutex);
|
|
||||||
mutex = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
S9xSetSamplesAvailableCallback (NULL, NULL);
|
S9xSetSamplesAvailableCallback (NULL, NULL);
|
||||||
|
|
||||||
Pa_Terminate ();
|
Pa_Terminate ();
|
||||||
|
|
||||||
|
if (sound_buffer)
|
||||||
|
{
|
||||||
|
free (sound_buffer);
|
||||||
|
sound_buffer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,8 +161,11 @@ S9xPortAudioSoundDriver::open_device (void)
|
||||||
Settings.SoundPlaybackRate,
|
Settings.SoundPlaybackRate,
|
||||||
0,
|
0,
|
||||||
paNoFlag,
|
paNoFlag,
|
||||||
port_audio_callback,
|
NULL,
|
||||||
this);
|
NULL);
|
||||||
|
|
||||||
|
int frames = Pa_GetStreamWriteAvailable (audio_stream);
|
||||||
|
output_buffer_size = frames_to_bytes (frames);
|
||||||
|
|
||||||
if (err == paNoError)
|
if (err == paNoError)
|
||||||
{
|
{
|
||||||
|
@ -195,7 +186,6 @@ S9xPortAudioSoundDriver::open_device (void)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex = g_mutex_new ();
|
|
||||||
S9xSetSamplesAvailableCallback (port_audio_samples_available_callback, this);
|
S9xSetSamplesAvailableCallback (port_audio_samples_available_callback, this);
|
||||||
|
|
||||||
fflush (stdout);
|
fflush (stdout);
|
||||||
|
@ -207,11 +197,41 @@ S9xPortAudioSoundDriver::open_device (void)
|
||||||
void
|
void
|
||||||
S9xPortAudioSoundDriver::samples_available (void)
|
S9xPortAudioSoundDriver::samples_available (void)
|
||||||
{
|
{
|
||||||
g_mutex_lock (mutex);
|
int frames;
|
||||||
|
int bytes;
|
||||||
|
|
||||||
|
frames = Pa_GetStreamWriteAvailable (audio_stream);
|
||||||
|
|
||||||
|
if (Settings.DynamicRateControl)
|
||||||
|
{
|
||||||
|
S9xUpdateDynamicRate (frames_to_bytes (frames), output_buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
S9xFinalizeSamples ();
|
S9xFinalizeSamples ();
|
||||||
|
|
||||||
g_mutex_unlock (mutex);
|
if (Settings.DynamicRateControl)
|
||||||
|
{
|
||||||
|
// Using rate control, we should always keep the emulator's sound buffers empty to
|
||||||
|
// maintain an accurate measurement.
|
||||||
|
if (frames < (S9xGetSampleCount () >> (Settings.Stereo ? 1 : 0)))
|
||||||
|
{
|
||||||
|
S9xClearSamples ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
frames = MIN (frames, S9xGetSampleCount () >> (Settings.Stereo ? 1 : 0));
|
||||||
|
bytes = frames_to_bytes (frames);
|
||||||
|
|
||||||
|
if (sound_buffer_size < bytes || sound_buffer == NULL)
|
||||||
|
{
|
||||||
|
sound_buffer = (uint8 *) realloc (sound_buffer, bytes);
|
||||||
|
sound_buffer_size = bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
S9xMixSamples (sound_buffer, frames << (Settings.Stereo ? 1 : 0));
|
||||||
|
|
||||||
|
Pa_WriteStream (audio_stream, sound_buffer, frames);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,9 @@ class S9xPortAudioSoundDriver : public S9xSoundDriver
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PaStream *audio_stream;
|
PaStream *audio_stream;
|
||||||
GMutex *mutex;
|
int sound_buffer_size;
|
||||||
|
uint8 *sound_buffer;
|
||||||
|
int output_buffer_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,9 @@ pulse_samples_available (void *data)
|
||||||
|
|
||||||
S9xPulseSoundDriver::S9xPulseSoundDriver (void)
|
S9xPulseSoundDriver::S9xPulseSoundDriver (void)
|
||||||
{
|
{
|
||||||
pulse = NULL;
|
mainloop = NULL;
|
||||||
buffer = NULL;
|
context = NULL;
|
||||||
|
stream = NULL;
|
||||||
buffer_size = 0;
|
buffer_size = 0;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -29,19 +30,24 @@ S9xPulseSoundDriver::init (void)
|
||||||
void
|
void
|
||||||
S9xPulseSoundDriver::terminate (void)
|
S9xPulseSoundDriver::terminate (void)
|
||||||
{
|
{
|
||||||
stop ();
|
|
||||||
|
|
||||||
S9xSetSamplesAvailableCallback (NULL, NULL);
|
S9xSetSamplesAvailableCallback (NULL, NULL);
|
||||||
|
|
||||||
if (pulse)
|
if (stream)
|
||||||
{
|
{
|
||||||
pa_simple_free (pulse);
|
pa_stream_disconnect (stream);
|
||||||
|
pa_stream_unref (stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buffer)
|
if (context)
|
||||||
{
|
{
|
||||||
delete[] buffer;
|
pa_context_disconnect (context);
|
||||||
buffer = NULL;
|
pa_context_unref (context);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mainloop)
|
||||||
|
{
|
||||||
|
pa_threaded_mainloop_stop (mainloop);
|
||||||
|
pa_threaded_mainloop_free (mainloop);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -59,12 +65,73 @@ S9xPulseSoundDriver::stop (void)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
S9xPulseSoundDriver::lock (void)
|
||||||
|
{
|
||||||
|
pa_threaded_mainloop_lock (mainloop);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
S9xPulseSoundDriver::unlock (void)
|
||||||
|
{
|
||||||
|
pa_threaded_mainloop_unlock (mainloop);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
S9xPulseSoundDriver::wait (void)
|
||||||
|
{
|
||||||
|
pa_threaded_mainloop_wait (mainloop);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
context_state_cb (pa_context *c, void *userdata)
|
||||||
|
{
|
||||||
|
S9xPulseSoundDriver *driver = (S9xPulseSoundDriver *) userdata;
|
||||||
|
int state;
|
||||||
|
|
||||||
|
state = pa_context_get_state (c);
|
||||||
|
|
||||||
|
if (state == PA_CONTEXT_READY ||
|
||||||
|
state == PA_CONTEXT_FAILED ||
|
||||||
|
state == PA_CONTEXT_TERMINATED)
|
||||||
|
{
|
||||||
|
pa_threaded_mainloop_signal (driver->mainloop, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
stream_state_callback (pa_stream *p, void *userdata)
|
||||||
|
{
|
||||||
|
S9xPulseSoundDriver *driver = (S9xPulseSoundDriver *) userdata;
|
||||||
|
int state;
|
||||||
|
|
||||||
|
state = pa_stream_get_state (p);
|
||||||
|
|
||||||
|
if (state == PA_STREAM_READY ||
|
||||||
|
state == PA_STREAM_FAILED ||
|
||||||
|
state == PA_STREAM_TERMINATED)
|
||||||
|
{
|
||||||
|
pa_threaded_mainloop_signal (driver->mainloop, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bool8
|
bool8
|
||||||
S9xPulseSoundDriver::open_device (void)
|
S9xPulseSoundDriver::open_device (void)
|
||||||
{
|
{
|
||||||
int err;
|
int err = PA_ERR_UNKNOWN;
|
||||||
pa_sample_spec ss;
|
pa_sample_spec ss;
|
||||||
pa_buffer_attr buffer_attr;
|
pa_buffer_attr buffer_attr;
|
||||||
|
const pa_buffer_attr *actual_buffer_attr;
|
||||||
|
|
||||||
ss.channels = Settings.Stereo ? 2 : 1;
|
ss.channels = Settings.Stereo ? 2 : 1;
|
||||||
ss.format = Settings.SixteenBitSound ? PA_SAMPLE_S16NE : PA_SAMPLE_U8;
|
ss.format = Settings.SixteenBitSound ? PA_SAMPLE_S16NE : PA_SAMPLE_U8;
|
||||||
|
@ -76,35 +143,88 @@ S9xPulseSoundDriver::open_device (void)
|
||||||
buffer_attr.minreq = -1;
|
buffer_attr.minreq = -1;
|
||||||
buffer_attr.prebuf = -1;
|
buffer_attr.prebuf = -1;
|
||||||
|
|
||||||
printf ("PulseAudio sound driver initializing...\n");
|
printf ("PulseAudio sound driver initializing...");
|
||||||
|
|
||||||
printf (" --> (%dhz, %s %s, %dms)...",
|
printf (" --> (%dhz, %s %s, %dms)...\n",
|
||||||
Settings.SoundPlaybackRate,
|
Settings.SoundPlaybackRate,
|
||||||
Settings.SixteenBitSound ? "16-bit" : "8-bit",
|
Settings.SixteenBitSound ? "16-bit" : "8-bit",
|
||||||
Settings.Stereo ? "Stereo" : "Mono",
|
Settings.Stereo ? "Stereo" : "Mono",
|
||||||
gui_config->sound_buffer_size);
|
gui_config->sound_buffer_size);
|
||||||
|
fflush (stdout);
|
||||||
|
|
||||||
pulse = pa_simple_new (NULL,
|
mainloop = pa_threaded_mainloop_new ();
|
||||||
"Snes9x",
|
if (!mainloop)
|
||||||
PA_STREAM_PLAYBACK,
|
|
||||||
NULL,
|
|
||||||
"Game",
|
|
||||||
&ss,
|
|
||||||
NULL,
|
|
||||||
&buffer_attr,
|
|
||||||
&err);
|
|
||||||
|
|
||||||
if (!pulse)
|
|
||||||
{
|
{
|
||||||
printf ("Failed: %s\n", pa_strerror (err));
|
fprintf (stderr, "Failed to create mainloop.\n");
|
||||||
return FALSE;
|
goto error0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context = pa_context_new (pa_threaded_mainloop_get_api (mainloop), "Snes9x");
|
||||||
|
if (!context)
|
||||||
|
goto error1;
|
||||||
|
|
||||||
|
pa_context_set_state_callback (context, context_state_cb, this);
|
||||||
|
if ((err = pa_context_connect (context, NULL, PA_CONTEXT_NOFLAGS, NULL)) != 0)
|
||||||
|
goto error2;
|
||||||
|
|
||||||
|
lock ();
|
||||||
|
|
||||||
|
if ((err = pa_threaded_mainloop_start (mainloop)) != 0)
|
||||||
|
goto error2;
|
||||||
|
wait ();
|
||||||
|
|
||||||
|
if ((err = pa_context_get_state (context)) != PA_CONTEXT_READY)
|
||||||
|
{
|
||||||
|
printf ("Coundn't create context: State: %d\n", err);
|
||||||
|
goto error2;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream = pa_stream_new (context, "Game", &ss, NULL);
|
||||||
|
|
||||||
|
if (!stream)
|
||||||
|
goto error2;
|
||||||
|
|
||||||
|
pa_stream_set_state_callback (stream, stream_state_callback, this);
|
||||||
|
|
||||||
|
if (pa_stream_connect_playback (stream,
|
||||||
|
NULL,
|
||||||
|
&buffer_attr,
|
||||||
|
PA_STREAM_ADJUST_LATENCY,
|
||||||
|
NULL,
|
||||||
|
NULL) < 0)
|
||||||
|
goto error3;
|
||||||
|
wait ();
|
||||||
|
|
||||||
|
if (pa_stream_get_state (stream) != PA_STREAM_READY)
|
||||||
|
{
|
||||||
|
goto error3;
|
||||||
|
}
|
||||||
|
|
||||||
|
actual_buffer_attr = pa_stream_get_buffer_attr (stream);
|
||||||
|
|
||||||
|
buffer_size = actual_buffer_attr->tlength;
|
||||||
|
|
||||||
printf ("OK\n");
|
printf ("OK\n");
|
||||||
|
|
||||||
S9xSetSamplesAvailableCallback (pulse_samples_available, this);
|
S9xSetSamplesAvailableCallback (pulse_samples_available, this);
|
||||||
|
|
||||||
|
unlock ();
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
|
error3:
|
||||||
|
pa_stream_disconnect (stream);
|
||||||
|
pa_stream_unref (stream);
|
||||||
|
error2:
|
||||||
|
pa_context_disconnect (context);
|
||||||
|
pa_context_unref (context);
|
||||||
|
unlock ();
|
||||||
|
error1:
|
||||||
|
pa_threaded_mainloop_free (mainloop);
|
||||||
|
error0:
|
||||||
|
printf ("Failed: %s\n", pa_strerror (err));
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -116,30 +236,45 @@ S9xPulseSoundDriver::mix (void)
|
||||||
void
|
void
|
||||||
S9xPulseSoundDriver::samples_available (void)
|
S9xPulseSoundDriver::samples_available (void)
|
||||||
{
|
{
|
||||||
int bytes, err;
|
size_t bytes;
|
||||||
|
int samples;
|
||||||
|
const pa_buffer_attr *buffer_attr;
|
||||||
|
void *output_buffer = NULL;
|
||||||
|
|
||||||
|
lock ();
|
||||||
|
bytes = pa_stream_writable_size (stream);
|
||||||
|
buffer_attr = pa_stream_get_buffer_attr (stream);
|
||||||
|
unlock ();
|
||||||
|
|
||||||
|
buffer_size = buffer_attr->tlength;
|
||||||
|
|
||||||
|
if (Settings.DynamicRateControl)
|
||||||
|
{
|
||||||
|
S9xUpdateDynamicRate (bytes, buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
S9xFinalizeSamples ();
|
S9xFinalizeSamples ();
|
||||||
|
|
||||||
bytes = (S9xGetSampleCount () << (Settings.SixteenBitSound ? 1 : 0));
|
samples = S9xGetSampleCount ();
|
||||||
|
|
||||||
if (bytes <= 128)
|
if (Settings.DynamicRateControl)
|
||||||
return;
|
|
||||||
|
|
||||||
if (buffer_size < bytes || buffer == NULL)
|
|
||||||
{
|
{
|
||||||
delete[] buffer;
|
if ((int) bytes < (samples << (Settings.SixteenBitSound ? 1 : 0)))
|
||||||
buffer = new uint8[bytes];
|
{
|
||||||
buffer_size = bytes;
|
S9xClearSamples ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
S9xMixSamples (buffer, bytes >> (Settings.SixteenBitSound ? 1 : 0));
|
bytes = MIN (bytes, (samples << (Settings.SixteenBitSound ? 1 : 0)));
|
||||||
|
|
||||||
/* PulseAudio-simple has no write indicator, so we don't know when the
|
lock ();
|
||||||
buffer is full. So we just drop the audio when we're in Turbo Mode. */
|
|
||||||
if (Settings.TurboMode)
|
|
||||||
return;
|
|
||||||
|
|
||||||
pa_simple_write (pulse, buffer, bytes, &err);
|
pa_stream_begin_write (stream, &output_buffer, &bytes);
|
||||||
|
S9xMixSamples ((uint8 *) output_buffer, bytes >> (Settings.SixteenBitSound ? 1 : 0));
|
||||||
|
pa_stream_write (stream, output_buffer, bytes, NULL, 0, PA_SEEK_RELATIVE);
|
||||||
|
|
||||||
|
unlock ();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
|
|
||||||
#include "gtk_sound.h"
|
#include "gtk_sound.h"
|
||||||
#include "gtk_sound_driver.h"
|
#include "gtk_sound_driver.h"
|
||||||
#include "pulse/simple.h"
|
#include "pulse/pulseaudio.h"
|
||||||
#include "pulse/error.h"
|
|
||||||
|
|
||||||
class S9xPulseSoundDriver : public S9xSoundDriver
|
class S9xPulseSoundDriver : public S9xSoundDriver
|
||||||
{
|
{
|
||||||
|
@ -17,10 +16,15 @@ class S9xPulseSoundDriver : public S9xSoundDriver
|
||||||
void stop (void);
|
void stop (void);
|
||||||
void mix (void);
|
void mix (void);
|
||||||
void samples_available (void);
|
void samples_available (void);
|
||||||
|
void lock (void);
|
||||||
|
void unlock (void);
|
||||||
|
void wait (void);
|
||||||
|
|
||||||
|
pa_threaded_mainloop *mainloop;
|
||||||
|
pa_context *context;
|
||||||
|
pa_stream *stream;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
pa_simple *pulse;
|
|
||||||
uint8 *buffer;
|
|
||||||
int buffer_size;
|
int buffer_size;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -150,6 +150,13 @@
|
||||||
<property name="step_increment">0.01</property>
|
<property name="step_increment">0.01</property>
|
||||||
<property name="page_increment">0.10000000000000001</property>
|
<property name="page_increment">0.10000000000000001</property>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="GtkAdjustment" id="dynamic_rate_adjustment">
|
||||||
|
<property name="lower">0.001</property>
|
||||||
|
<property name="upper">0.100</property>
|
||||||
|
<property name="value">0.010</property>
|
||||||
|
<property name="step_increment">0.001</property>
|
||||||
|
<property name="page_increment">0.010</property>
|
||||||
|
</object>
|
||||||
<object class="GtkAdjustment" id="adjustment13">
|
<object class="GtkAdjustment" id="adjustment13">
|
||||||
<property name="lower">-1</property>
|
<property name="lower">-1</property>
|
||||||
<property name="upper">1</property>
|
<property name="upper">1</property>
|
||||||
|
@ -4008,6 +4015,23 @@
|
||||||
<property name="position">1</property>
|
<property name="position">1</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<object class="GtkCheckButton" id="dynamic_rate_control">
|
||||||
|
<property name="label" translatable="yes">Dynamic rate control</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">False</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Adjust input rate automatically</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkCheckButton" id="mute_sound_check">
|
<object class="GtkCheckButton" id="mute_sound_check">
|
||||||
<property name="label" translatable="yes">Mute sound output</property>
|
<property name="label" translatable="yes">Mute sound output</property>
|
||||||
|
@ -4021,7 +4045,7 @@
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
<property name="fill">False</property>
|
<property name="fill">False</property>
|
||||||
<property name="position">2</property>
|
<property name="position">3</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
|
@ -4037,7 +4061,7 @@
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
<property name="fill">False</property>
|
<property name="fill">False</property>
|
||||||
<property name="position">3</property>
|
<property name="position">4</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
|
@ -4053,14 +4077,14 @@
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
<property name="fill">False</property>
|
<property name="fill">False</property>
|
||||||
<property name="position">4</property>
|
<property name="position">5</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkTable" id="table7">
|
<object class="GtkTable" id="table7">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="n_rows">4</property>
|
<property name="n_rows">5</property>
|
||||||
<property name="n_columns">2</property>
|
<property name="n_columns">2</property>
|
||||||
<property name="column_spacing">10</property>
|
<property name="column_spacing">10</property>
|
||||||
<property name="row_spacing">5</property>
|
<property name="row_spacing">5</property>
|
||||||
|
@ -4135,6 +4159,50 @@
|
||||||
<property name="y_options">GTK_FILL</property>
|
<property name="y_options">GTK_FILL</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="dynamic_rate_limit_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="xalign">0</property>
|
||||||
|
<property name="label" translatable="yes">Dynamic rate limit:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="top_attach">4</property>
|
||||||
|
<property name="bottom_attach">5</property>
|
||||||
|
<property name="x_options">GTK_FILL</property>
|
||||||
|
<property name="y_options">GTK_FILL</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkHBox" id="hbox_dynamic_rate">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkSpinButton" id="dynamic_rate_limit">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="primary_icon_activatable">False</property>
|
||||||
|
<property name="secondary_icon_activatable">False</property>
|
||||||
|
<property name="primary_icon_sensitive">True</property>
|
||||||
|
<property name="secondary_icon_sensitive">True</property>
|
||||||
|
<property name="digits">3</property>
|
||||||
|
<property name="adjustment">dynamic_rate_adjustment</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">1</property>
|
||||||
|
<property name="top_attach">4</property>
|
||||||
|
<property name="bottom_attach">5</property>
|
||||||
|
<property name="y_options">GTK_FILL</property>
|
||||||
|
<property name="x_options">GTK_FILL</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel" id="sound_input_rate_label">
|
<object class="GtkLabel" id="sound_input_rate_label">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
@ -4229,7 +4297,7 @@
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
<property name="fill">True</property>
|
<property name="fill">True</property>
|
||||||
<property name="position">5</property>
|
<property name="position">6</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
|
|
@ -422,6 +422,8 @@ void S9xLoadConfigFiles (char **argv, int argc)
|
||||||
Settings.SoundPlaybackRate = conf.GetUInt("Sound::Rate", 32000);
|
Settings.SoundPlaybackRate = conf.GetUInt("Sound::Rate", 32000);
|
||||||
Settings.SoundInputRate = conf.GetUInt("Sound::InputRate", 32000);
|
Settings.SoundInputRate = conf.GetUInt("Sound::InputRate", 32000);
|
||||||
Settings.Mute = conf.GetBool("Sound::Mute", false);
|
Settings.Mute = conf.GetBool("Sound::Mute", false);
|
||||||
|
Settings.DynamicRateControl = conf.GetBool("Sound::DynamicRateControl", false);
|
||||||
|
Settings.DynamicRateLimit = conf.GetUInt("Sound::DynamicRateLimit", 1000);
|
||||||
|
|
||||||
// Display
|
// Display
|
||||||
|
|
||||||
|
@ -849,7 +851,7 @@ char * S9xParseArgs (char **argv, int argc)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// HACKING OR DEBUGGING OPTIONS
|
// HACKING OR DEBUGGING OPTIONS
|
||||||
|
|
||||||
#ifdef DEBUGGER
|
#ifdef DEBUGGER
|
||||||
if (!strcasecmp(argv[i], "-debug"))
|
if (!strcasecmp(argv[i], "-debug"))
|
||||||
CPU.Flags |= DEBUG_MODE_FLAG;
|
CPU.Flags |= DEBUG_MODE_FLAG;
|
||||||
|
|
2
snes9x.h
2
snes9x.h
|
@ -407,6 +407,8 @@ struct SSettings
|
||||||
bool8 Stereo;
|
bool8 Stereo;
|
||||||
bool8 ReverseStereo;
|
bool8 ReverseStereo;
|
||||||
bool8 Mute;
|
bool8 Mute;
|
||||||
|
bool8 DynamicRateControl;
|
||||||
|
uint32 DynamicRateLimit; /* Multiplied by 100000 */
|
||||||
|
|
||||||
bool8 SupportHiRes;
|
bool8 SupportHiRes;
|
||||||
bool8 Transparency;
|
bool8 Transparency;
|
||||||
|
|
Loading…
Reference in New Issue