aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAutechre2022-03-22 15:22:37 +0100
committerGitHub2022-03-22 15:22:37 +0100
commita279e2b216919d79777928bad01e3d92039ad0fb (patch)
tree652add684a4e5d7cda11381507618f57f4c292d8
parentabd072aa370620112a88cc7b350c2dfb1b58185a (diff)
parentd22597e9032ee8024b477af78000704ec072e49b (diff)
downloadsnes9x2005-a279e2b216919d79777928bad01e3d92039ad0fb.tar.gz
snes9x2005-a279e2b216919d79777928bad01e3d92039ad0fb.tar.bz2
snes9x2005-a279e2b216919d79777928bad01e3d92039ad0fb.zip
Merge pull request #95 from jdgleaver/audio-sample-pacing
Fix audio sample pacing
-rw-r--r--libretro.c216
-rw-r--r--libretro_core_options.h2
-rw-r--r--source/apu_blargg.c6
-rw-r--r--source/memmap.c8
-rw-r--r--source/soundux.c2
5 files changed, 165 insertions, 69 deletions
diff --git a/libretro.c b/libretro.c
index ae4cd85..2b04506 100644
--- a/libretro.c
+++ b/libretro.c
@@ -48,15 +48,20 @@ bool overclock_cycles = false;
bool reduce_sprite_flicker = false;
int one_c, slow_one_c, two_c;
-#ifdef _WIN32
- char slash = '\\';
+#define VIDEO_REFRESH_RATE_PAL (SNES_CLOCK_SPEED * 6.0 / (SNES_CYCLES_PER_SCANLINE * SNES_MAX_PAL_VCOUNTER))
+#define VIDEO_REFRESH_RATE_NTSC (SNES_CLOCK_SPEED * 6.0 / (SNES_CYCLES_PER_SCANLINE * SNES_MAX_NTSC_VCOUNTER))
+#define AUDIO_SAMPLE_RATE 32040
+
+static int16_t *audio_out_buffer = NULL;
+#ifdef USE_BLARGG_APU
+static size_t audio_out_buffer_size = 0;
+static size_t audio_out_buffer_pos = 0;
+static size_t audio_batch_frames_max = (1 << 16);
#else
- char slash = '/';
+static float audio_samples_per_frame = 0.0f;
+static float audio_samples_accumulator = 0.0f;
#endif
-static int32_t samples_per_frame = 0;
-static int32_t samplerate = (((SNES_CLOCK_SPEED * 6) / (32 * ONE_APU_CYCLE)));
-
static unsigned frameskip_type = 0;
static unsigned frameskip_threshold = 0;
static uint16_t frameskip_counter = 0;
@@ -266,7 +271,10 @@ static void init_sfc_setting(void)
{
memset(&Settings, 0, sizeof(Settings));
Settings.JoystickEnabled = false;
- Settings.SoundPlaybackRate = samplerate;
+ Settings.SoundPlaybackRate = AUDIO_SAMPLE_RATE;
+#ifdef USE_BLARGG_APU
+ Settings.SoundInputRate = AUDIO_SAMPLE_RATE;
+#endif
Settings.CyclesPercentage = 100;
Settings.DisableSoundEcho = false;
@@ -288,20 +296,127 @@ static void init_sfc_setting(void)
Settings.HBlankStart = (256 * Settings.H_Max) / SNES_HCOUNTER_MAX;
}
+static void audio_out_buffer_init(void)
+{
+ float refresh_rate = (float)((Settings.PAL) ?
+ VIDEO_REFRESH_RATE_PAL : VIDEO_REFRESH_RATE_NTSC);
+ float samples_per_frame = (float)AUDIO_SAMPLE_RATE / refresh_rate;
+ size_t buffer_size = ((size_t)samples_per_frame + 1) << 1;
+
+ audio_out_buffer = (int16_t *)malloc(buffer_size * sizeof(int16_t));
+#ifdef USE_BLARGG_APU
+ audio_out_buffer_size = buffer_size;
+ audio_out_buffer_pos = 0;
+ audio_batch_frames_max = (1 << 16);
+#else
+ audio_samples_per_frame = samples_per_frame;
+ audio_samples_accumulator = 0.0f;
+#endif
+}
+
+static void audio_out_buffer_deinit(void)
+{
+ if (audio_out_buffer)
+ free(audio_out_buffer);
+ audio_out_buffer = NULL;
+
+#ifdef USE_BLARGG_APU
+ audio_out_buffer_size = 0;
+ audio_out_buffer_pos = 0;
+ audio_batch_frames_max = (1 << 16);
+#else
+ audio_samples_per_frame = 0.0f;
+ audio_samples_accumulator = 0.0f;
+#endif
+}
+
#ifdef USE_BLARGG_APU
static void S9xAudioCallback(void)
{
- size_t avail;
- /* Just pick a big buffer. We won't use it all. */
- static int16_t audio_buf[0x20000];
+ size_t available_samples;
+ size_t buffer_capacity = audio_out_buffer_size -
+ audio_out_buffer_pos;
S9xFinalizeSamples();
- avail = S9xGetSampleCount();
- S9xMixSamples(audio_buf, avail);
- audio_batch_cb(audio_buf, avail >> 1);
+ available_samples = S9xGetSampleCount();
+
+ if (buffer_capacity < available_samples)
+ {
+ int16_t *tmp_buffer = NULL;
+ size_t tmp_buffer_size;
+ size_t i;
+
+ tmp_buffer_size = audio_out_buffer_size + (available_samples - buffer_capacity);
+ tmp_buffer_size = (tmp_buffer_size << 1) - (tmp_buffer_size >> 1);
+ tmp_buffer = (int16_t *)malloc(tmp_buffer_size * sizeof(int16_t));
+
+ for (i = 0; i < audio_out_buffer_pos; i++)
+ tmp_buffer[i] = audio_out_buffer[i];
+
+ free(audio_out_buffer);
+
+ audio_out_buffer = tmp_buffer;
+ audio_out_buffer_size = tmp_buffer_size;
+ }
+
+ S9xMixSamples(audio_out_buffer + audio_out_buffer_pos,
+ available_samples);
+ audio_out_buffer_pos += available_samples;
}
#endif
+static void audio_upload_samples(void)
+{
+ size_t available_frames;
+#ifdef USE_BLARGG_APU
+ int16_t *audio_out_buffer_ptr;
+
+ S9xAudioCallback();
+
+ audio_out_buffer_ptr = audio_out_buffer;
+ available_frames = audio_out_buffer_pos >> 1;
+
+ /* Since the audio output buffer can
+ * (theoretically) have an arbitrarily size,
+ * we must write it in chunks of the largest
+ * size supported by the frontend
+ * (this is not required for the non-blargg
+ * code path, since the buffer size has well
+ * defined limits in that case) */
+ while (available_frames > 0)
+ {
+ size_t frames_to_write = (available_frames >
+ audio_batch_frames_max) ?
+ audio_batch_frames_max :
+ available_frames;
+ size_t frames_written = audio_batch_cb(
+ audio_out_buffer_ptr, frames_to_write);
+
+ if ((frames_written < frames_to_write) &&
+ (frames_written > 0))
+ audio_batch_frames_max = frames_written;
+
+ available_frames -= frames_to_write;
+ audio_out_buffer_ptr += frames_to_write << 1;
+ }
+
+ audio_out_buffer_pos = 0;
+#else
+ available_frames = (size_t)audio_samples_per_frame;
+ audio_samples_accumulator += audio_samples_per_frame -
+ (float)available_frames;
+
+ if (audio_samples_accumulator > 1.0f)
+ {
+ available_frames += 1;
+ audio_samples_accumulator -= 1.0f;
+ }
+
+ S9xMixSamples(audio_out_buffer, available_frames << 1);
+ audio_batch_cb(audio_out_buffer, available_frames);
+#endif
+}
+
void retro_init(void)
{
struct retro_log_callback log;
@@ -326,7 +441,7 @@ void retro_init(void)
S9xInitDisplay();
S9xInitGFX();
#ifdef USE_BLARGG_APU
- S9xInitSound(1000, 0); /* just give it a 1 second buffer */
+ S9xInitSound(0, 0); /* Use default values */
S9xSetSamplesAvailableCallback(S9xAudioCallback);
#else
S9xInitSound();
@@ -351,6 +466,8 @@ void retro_deinit(void)
perf_cb.perf_log();
#endif
+ audio_out_buffer_deinit();
+
/* Reset globals (required for static builds) */
libretro_supports_option_categories = false;
libretro_supports_bitmasks = false;
@@ -404,20 +521,18 @@ uint32_t S9xReadJoypad(int32_t port)
static void check_variables(bool first_run)
{
struct retro_variable var;
- bool prev_force_ntsc;
- bool prev_force_pal;
bool prev_frameskip_type;
- var.key = "snes9x_2005_region";
- var.value = NULL;
-
- prev_force_ntsc = Settings.ForceNTSC;
- prev_force_pal = Settings.ForcePAL;
-
- if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
+ if (first_run)
{
- Settings.ForceNTSC = !strcmp(var.value, "NTSC");
- Settings.ForcePAL = !strcmp(var.value, "PAL");
+ var.key = "snes9x_2005_region";
+ var.value = NULL;
+
+ if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
+ {
+ Settings.ForceNTSC = !strcmp(var.value, "NTSC");
+ Settings.ForcePAL = !strcmp(var.value, "PAL");
+ }
}
var.key = "snes9x_2005_frameskip";
@@ -478,21 +593,15 @@ static void check_variables(bool first_run)
/* Reinitialise frameskipping, if required */
if (!first_run &&
- ((frameskip_type != prev_frameskip_type) ||
- (Settings.ForceNTSC != prev_force_ntsc) ||
- (Settings.ForcePAL != prev_force_pal)))
+ (frameskip_type != prev_frameskip_type))
retro_set_audio_buff_status_cb();
}
-static int32_t samples_to_play = 0;
void retro_run(void)
{
bool updated = false;
int result;
bool okay;
-#ifndef USE_BLARGG_APU
- static int16_t audio_buf[2048];
-#endif
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated)
check_variables(false);
@@ -578,20 +687,8 @@ void retro_run(void)
S9xMainLoop();
RETRO_PERFORMANCE_STOP(S9xMainLoop_func);
-#ifndef USE_BLARGG_APU
- samples_to_play += samples_per_frame;
-
- if (samples_to_play > 512)
- {
- S9xMixSamples(audio_buf, samples_to_play * 2);
- audio_batch_cb(audio_buf, samples_to_play);
- samples_to_play = 0;
- }
-#else
- S9xAudioCallback();
-#endif
-
#ifdef NO_VIDEO_OUTPUT
+ audio_upload_samples();
return;
#endif
@@ -616,6 +713,8 @@ void retro_run(void)
}
else
video_cb(NULL, IPPU.RenderedScreenWidth, IPPU.RenderedScreenHeight, GFX.Pitch);
+
+ audio_upload_samples();
}
bool S9xReadMousePosition(int32_t which1, int32_t* x, int32_t* y, uint32_t* buttons)
@@ -672,18 +771,15 @@ void retro_get_system_info(struct retro_system_info* info)
void retro_get_system_av_info(struct retro_system_av_info* info)
{
- info->geometry.base_width = 256;
- info->geometry.base_height = 224;
- info->geometry.max_width = 512;
- info->geometry.max_height = 512;
+ info->geometry.base_width = 256;
+ info->geometry.base_height = 224;
+ info->geometry.max_width = 512;
+ info->geometry.max_height = 512;
info->geometry.aspect_ratio = 4.0 / 3.0;
- if (!Settings.PAL)
- info->timing.fps = (SNES_CLOCK_SPEED * 6.0 / (SNES_CYCLES_PER_SCANLINE * SNES_MAX_NTSC_VCOUNTER));
- else
- info->timing.fps = (SNES_CLOCK_SPEED * 6.0 / (SNES_CYCLES_PER_SCANLINE * SNES_MAX_PAL_VCOUNTER));
-
- info->timing.sample_rate = samplerate;
+ info->timing.sample_rate = AUDIO_SAMPLE_RATE;
+ info->timing.fps = (Settings.PAL) ?
+ VIDEO_REFRESH_RATE_PAL : VIDEO_REFRESH_RATE_NTSC;
}
void retro_reset(void)
@@ -958,7 +1054,6 @@ static void init_descriptors(void)
bool retro_load_game(const struct retro_game_info* game)
{
- struct retro_system_av_info av_info;
if (!game)
return false;
@@ -976,14 +1071,11 @@ bool retro_load_game(const struct retro_game_info* game)
Settings.FrameTime = (Settings.PAL ? Settings.FrameTimePAL : Settings.FrameTimeNTSC);
retro_set_audio_buff_status_cb();
- retro_get_system_av_info(&av_info);
-
-#ifdef USE_BLARGG_APU
- Settings.SoundPlaybackRate = av_info.timing.sample_rate;
-#else
- samples_per_frame = av_info.timing.sample_rate / av_info.timing.fps;
+ audio_out_buffer_init();
+#ifndef USE_BLARGG_APU
S9xSetPlaybackRate(Settings.SoundPlaybackRate);
#endif
+
return true;
}
diff --git a/libretro_core_options.h b/libretro_core_options.h
index 9b89154..6f46d16 100644
--- a/libretro_core_options.h
+++ b/libretro_core_options.h
@@ -66,7 +66,7 @@ struct retro_core_option_v2_definition option_defs_us[] = {
{
"snes9x_2005_region",
- "Console Region",
+ "Console Region (Restart)",
NULL,
"Specify which region the system is from. 'PAL' is 50hz, 'NTSC' is 60hz. Games will run faster or slower than normal if the incorrect region is selected.",
NULL,
diff --git a/source/apu_blargg.c b/source/apu_blargg.c
index 20b8b50..cfa7aae 100644
--- a/source/apu_blargg.c
+++ b/source/apu_blargg.c
@@ -2849,7 +2849,7 @@ void spc_copy_state( uint8_t ** io, dsp_copy_func_t copy )
APU
***********************************************************************************/
-#define APU_DEFAULT_INPUT_RATE 32000
+#define APU_DEFAULT_INPUT_RATE 32040
#define APU_MINIMUM_SAMPLE_COUNT (512*8)
#define APU_MINIMUM_SAMPLE_BLOCK (128*8)
#define APU_NUMERATOR_NTSC 15664
@@ -3177,8 +3177,8 @@ bool S9xInitSound (int32_t buffer_ms, int32_t lag_ms)
lag_ms : allowable time-lag given in millisecond */
int32_t sample_count, lag_sample_count;
- sample_count = buffer_ms * 32000 / 1000;
- lag_sample_count = lag_ms * 32000 / 1000;
+ sample_count = buffer_ms * 32040 / 1000;
+ lag_sample_count = lag_ms * 32040 / 1000;
lag_master = lag_sample_count;
diff --git a/source/memmap.c b/source/memmap.c
index d7986e1..8db9f93 100644
--- a/source/memmap.c
+++ b/source/memmap.c
@@ -217,7 +217,7 @@ static int32_t ScoreLoROM(bool skip_header, int32_t romoff)
static char* Safe(const char* s)
{
- static char* safe;
+ static char* safe = NULL;
static int32_t safe_len = 0;
int32_t i;
int32_t len;
@@ -338,7 +338,7 @@ void S9xDeinitMemory(void)
Memory.BSRAM = NULL;
}
- for (t = 0; t < 2; t++)
+ for (t = 0; t <= TILE_8BIT; t++)
{
if (IPPU.TileCache[t])
{
@@ -351,6 +351,10 @@ void S9xDeinitMemory(void)
IPPU.TileCached[t] = NULL;
}
}
+
+ /* Ensure that we free the static char
+ * array allocated by Safe() */
+ Safe(NULL);
}
#ifndef LOAD_FROM_MEMORY
diff --git a/source/soundux.c b/source/soundux.c
index 42098ed..66b546b 100644
--- a/source/soundux.c
+++ b/source/soundux.c
@@ -211,7 +211,7 @@ void S9xSetEchoFeedback(int32_t feedback)
void S9xSetEchoDelay(int32_t delay)
{
- SoundData.echo_buffer_size = (512 * delay * so.playback_rate) / 32000;
+ SoundData.echo_buffer_size = (512 * delay * so.playback_rate) / 32040;
SoundData.echo_buffer_size <<= 1;
if (SoundData.echo_buffer_size)
SoundData.echo_ptr %= SoundData.echo_buffer_size;