diff --git a/libretro/libretro-common/include/libretro.h b/libretro/libretro-common/include/libretro.h index 7362725..4ca9887 100644 --- a/libretro/libretro-common/include/libretro.h +++ b/libretro/libretro-common/include/libretro.h @@ -967,6 +967,8 @@ enum retro_mod * core supports VFS before it starts handing out paths. * It is recomended to do so in retro_set_environment */ +#define RETRO_ENVIRONMENT_GET_AUDIO_VIDEO_ENABLE (47 | RETRO_ENVIRONMENT_EXPERIMENTAL) + /* Opaque file handle * Introduced in VFS API v1 */ struct retro_vfs_file_handle; diff --git a/libretro/libretro.c b/libretro/libretro.c index 1ecf14d..ff396b4 100644 --- a/libretro/libretro.c +++ b/libretro/libretro.c @@ -869,7 +869,16 @@ void retro_run (void) bool updated = false; if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated) check_variables(); - + + int result = -1; + bool okay = environ_cb(RETRO_ENVIRONMENT_GET_AUDIO_VIDEO_ENABLE, &result); + if (!okay) result |= 3; + bool audioEnabled = 0 != (result & 2); + bool videoEnabled = 0 != (result & 1); + + IPPU.RenderThisFrame = videoEnabled; + S9xSetSoundMute(!audioEnabled); + poll_cb(); report_buttons(); S9xMainLoop(); diff --git a/src/apu.c b/src/apu.c index 29c542d..a377f7b 100644 --- a/src/apu.c +++ b/src/apu.c @@ -3284,19 +3284,22 @@ static INLINE void resampler_resize (int num_samples) bool8 S9xMixSamples (short *buffer, unsigned sample_count) { - if (S9xGetSampleCount() >= (sample_count + lag)) + if (!Settings.Mute) { - resampler_read(buffer, sample_count); - if (lag == lag_master) - lag = 0; - } - else - { - memset(buffer, 0, sample_count << 1); - if (lag == 0) - lag = lag_master; + if (S9xGetSampleCount() >= (sample_count + lag)) + { + resampler_read(buffer, sample_count); + if (lag == lag_master) + lag = 0; + } + else + { + memset(buffer, 0, sample_count << 1); + if (lag == 0) + lag = lag_master; - return (FALSE); + return (FALSE); + } } return (TRUE); @@ -3344,15 +3347,17 @@ static void spc_set_output( short* out, int size ) void S9xFinalizeSamples (void) { bool8 ret; + if (!Settings.Mute) + { + ret = resampler_push(landing_buffer, SPC_SAMPLE_COUNT()); + sound_in_sync = FALSE; - ret = resampler_push(landing_buffer, SPC_SAMPLE_COUNT()); - sound_in_sync = FALSE; + /* We weren't able to process the entire buffer. Potential overrun. */ + if (!ret && Settings.SoundSync) + return; + } - /* We weren't able to process the entire buffer. Potential overrun. */ - if (!ret && Settings.SoundSync) - return; - - if (!Settings.SoundSync || (SPACE_EMPTY() >= SPACE_FILLED())) + if (!Settings.SoundSync || (SPACE_EMPTY() >= SPACE_FILLED() || Settings.Mute)) sound_in_sync = TRUE; m.extra_clocks &= CLOCKS_PER_SAMPLE - 1; @@ -3439,6 +3444,11 @@ bool8 S9xInitSound (int buffer_ms, int lag_ms) return TRUE; } +void S9xSetSoundMute(bool8 mute) +{ + Settings.Mute = mute; +} + /* Must be called once before using */ static unsigned char cycle_table [128] = {/* 01 23 45 67 89 AB CD EF */ diff --git a/src/controls.c b/src/controls.c index 7bed588..193f661 100644 --- a/src/controls.c +++ b/src/controls.c @@ -1460,7 +1460,8 @@ void S9xControlEOF (void) case MOUSE1: c = &mouse[i - MOUSE0].crosshair; if(Settings.Crosshair) - S9xDrawCrosshair(S9xGetCrosshair(c->img), c->fg, c->bg, mouse[i - MOUSE0].cur_x, mouse[i - MOUSE0].cur_y); + if (IPPU.RenderThisFrame) + S9xDrawCrosshair(S9xGetCrosshair(c->img), c->fg, c->bg, mouse[i - MOUSE0].cur_x, mouse[i - MOUSE0].cur_y); break; case SUPERSCOPE: @@ -1471,7 +1472,8 @@ void S9xControlEOF (void) c = &superscope.crosshair; if(Settings.Crosshair) - S9xDrawCrosshair(S9xGetCrosshair(c->img), c->fg, c->bg, superscope.x, superscope.y); + if (IPPU.RenderThisFrame) + S9xDrawCrosshair(S9xGetCrosshair(c->img), c->fg, c->bg, superscope.x, superscope.y); } break; @@ -1481,7 +1483,8 @@ void S9xControlEOF (void) { c = &justifier.crosshair[1]; if(Settings.Crosshair) - S9xDrawCrosshair(S9xGetCrosshair(c->img), c->fg, c->bg, justifier.x[1], justifier.y[1]); + if (IPPU.RenderThisFrame) + S9xDrawCrosshair(S9xGetCrosshair(c->img), c->fg, c->bg, justifier.x[1], justifier.y[1]); } i = (justifier.buttons & JUSTIFIER_SELECT) ? 1 : 0; @@ -1500,7 +1503,8 @@ do_justifier: { c = &justifier.crosshair[0]; if(Settings.Crosshair) - S9xDrawCrosshair(S9xGetCrosshair(c->img), c->fg, c->bg, justifier.x[0], justifier.y[0]); + if (IPPU.RenderThisFrame) + S9xDrawCrosshair(S9xGetCrosshair(c->img), c->fg, c->bg, justifier.x[0], justifier.y[0]); } } diff --git a/src/cpuexec.c b/src/cpuexec.c index 30b4aef..dda9706 100644 --- a/src/cpuexec.c +++ b/src/cpuexec.c @@ -424,58 +424,77 @@ static INLINE void speedhacks_manager (void) static void S9xEndScreenRefresh (void) { - FLUSH_REDRAW(); + if (IPPU.RenderThisFrame) + { + FLUSH_REDRAW(); - PPU.GunVLatch = 1000; /* i.e., never latch */ - PPU.GunHLatch = 0; - - if (!Settings.NormalControls && pad_read) - S9xControlEOF(); - - pad_read = FALSE; - - if(Settings.SpeedhackGameID > SPEEDHACK_NONE) - speedhacks_manager(); - - if (!(GFX.DoInterlace && GFX.InterlaceFrame == 0)) - { - S9xDeinitUpdate(IPPU.RenderedScreenWidth, IPPU.RenderedScreenHeight); + if (!(GFX.DoInterlace && GFX.InterlaceFrame == 0)) + { + S9xDeinitUpdate(IPPU.RenderedScreenWidth, IPPU.RenderedScreenHeight); +#ifdef LAGFIX + finishedFrame = true; +#endif + } + } + else + { #ifdef LAGFIX finishedFrame = true; #endif - } + } + PPU.GunVLatch = 1000; /* i.e., never latch */ + PPU.GunHLatch = 0; + + if (!Settings.NormalControls && pad_read) + S9xControlEOF(); + + pad_read = FALSE; + + if (Settings.SpeedhackGameID > SPEEDHACK_NONE) + speedhacks_manager(); S9xApplyCheats(); } static void RenderLine (uint8 C) { - LineData[C].BG[0].VOffset = PPU.BG[0].VOffset + 1; - LineData[C].BG[0].HOffset = PPU.BG[0].HOffset; - LineData[C].BG[1].VOffset = PPU.BG[1].VOffset + 1; - LineData[C].BG[1].HOffset = PPU.BG[1].HOffset; - - if (PPU.BGMode == 7) + if (IPPU.RenderThisFrame) { - struct SLineMatrixData *p = &LineMatrixData[C]; - p->MatrixA = PPU.MatrixA; - p->MatrixB = PPU.MatrixB; - p->MatrixC = PPU.MatrixC; - p->MatrixD = PPU.MatrixD; - p->CentreX = PPU.CentreX; - p->CentreY = PPU.CentreY; - p->M7HOFS = PPU.M7HOFS; - p->M7VOFS = PPU.M7VOFS; + LineData[C].BG[0].VOffset = PPU.BG[0].VOffset + 1; + LineData[C].BG[0].HOffset = PPU.BG[0].HOffset; + LineData[C].BG[1].VOffset = PPU.BG[1].VOffset + 1; + LineData[C].BG[1].HOffset = PPU.BG[1].HOffset; + + if (PPU.BGMode == 7) + { + struct SLineMatrixData *p = &LineMatrixData[C]; + p->MatrixA = PPU.MatrixA; + p->MatrixB = PPU.MatrixB; + p->MatrixC = PPU.MatrixC; + p->MatrixD = PPU.MatrixD; + p->CentreX = PPU.CentreX; + p->CentreY = PPU.CentreY; + p->M7HOFS = PPU.M7HOFS; + p->M7VOFS = PPU.M7VOFS; + } + else + { + LineData[C].BG[2].VOffset = PPU.BG[2].VOffset + 1; + LineData[C].BG[2].HOffset = PPU.BG[2].HOffset; + LineData[C].BG[3].VOffset = PPU.BG[3].VOffset + 1; + LineData[C].BG[3].HOffset = PPU.BG[3].HOffset; + } + + IPPU.CurrentLine = C + 1; } else { - LineData[C].BG[2].VOffset = PPU.BG[2].VOffset + 1; - LineData[C].BG[2].HOffset = PPU.BG[2].HOffset; - LineData[C].BG[3].VOffset = PPU.BG[3].VOffset + 1; - LineData[C].BG[3].HOffset = PPU.BG[3].HOffset; + // if we're not rendering this frame, we still need to update this + // XXX: Check ForceBlank? Or anything else? + if (IPPU.OBJChanged) + SetupOBJ(); + PPU.RangeTimeOver |= GFX.OBJLines[C].RTOFlags; } - - IPPU.CurrentLine = C + 1; } static INLINE void S9xReschedule (void) @@ -1090,38 +1109,41 @@ void S9xDoHEventProcessing (void) /* V = 1 */ if (CPU.V_Counter == FIRST_VISIBLE_LINE) { - GFX.InterlaceFrame = !GFX.InterlaceFrame; - - if (!GFX.DoInterlace || !GFX.InterlaceFrame) + if (IPPU.RenderThisFrame) { - /* S9x Start Screen Refresh */ - bool8 cond_1, cond_2; + GFX.InterlaceFrame = !GFX.InterlaceFrame; - GFX.DoInterlace -= (GFX.DoInterlace == TRUE); + if (!GFX.DoInterlace || !GFX.InterlaceFrame) + { + /* S9x Start Screen Refresh */ + bool8 cond_1, cond_2; - IPPU.Interlace = Memory.FillRAM[0x2133] & 1; - IPPU.InterlaceOBJ = Memory.FillRAM[0x2133] & 2; - IPPU.PseudoHires = Memory.FillRAM[0x2133] & 8; + GFX.DoInterlace -= (GFX.DoInterlace == TRUE); - cond_1 = (Settings.SupportHiRes && (PPU.BGMode == 5 || PPU.BGMode == 6 || IPPU.PseudoHires)); - cond_2 = (Settings.SupportHiRes && IPPU.Interlace); + IPPU.Interlace = Memory.FillRAM[0x2133] & 1; + IPPU.InterlaceOBJ = Memory.FillRAM[0x2133] & 2; + IPPU.PseudoHires = Memory.FillRAM[0x2133] & 8; - GFX.RealPPL = GFX.Pitch >> 1; - IPPU.RenderedScreenWidth = SNES_WIDTH << cond_1; - IPPU.RenderedScreenHeight = PPU.ScreenHeight << cond_2; - IPPU.DoubleWidthPixels = cond_1; - IPPU.DoubleHeightPixels = cond_2; + cond_1 = (Settings.SupportHiRes && (PPU.BGMode == 5 || PPU.BGMode == 6 || IPPU.PseudoHires)); + cond_2 = (Settings.SupportHiRes && IPPU.Interlace); - GFX.PPL = GFX.RealPPL << cond_2; - GFX.DoInterlace += cond_2; + GFX.RealPPL = GFX.Pitch >> 1; + IPPU.RenderedScreenWidth = SNES_WIDTH << cond_1; + IPPU.RenderedScreenHeight = PPU.ScreenHeight << cond_2; + IPPU.DoubleWidthPixels = cond_1; + IPPU.DoubleHeightPixels = cond_2; + + GFX.PPL = GFX.RealPPL << cond_2; + GFX.DoInterlace += cond_2; + } + + PPU.MosaicStart = 0; + PPU.RecomputeClipWindows = TRUE; + IPPU.PreviousLine = IPPU.CurrentLine = 0; + + memset(GFX.ZBuffer, 0, GFX.ScreenSize); + memset(GFX.SubZBuffer, 0, GFX.ScreenSize); } - - PPU.MosaicStart = 0; - PPU.RecomputeClipWindows = TRUE; - IPPU.PreviousLine = IPPU.CurrentLine = 0; - - memset(GFX.ZBuffer, 0, GFX.ScreenSize); - memset(GFX.SubZBuffer, 0, GFX.ScreenSize); } CPU.NextEvent = -1; diff --git a/src/ppu.c b/src/ppu.c index 295fbb1..373057f 100644 --- a/src/ppu.c +++ b/src/ppu.c @@ -354,7 +354,7 @@ static int objsize_array[8][4] = { {16, 32, 32, 32}, /*7*/ }; -static void SetupOBJ (void) +void SetupOBJ (void) { int Height, Y_two, SmallWidth, SmallHeight, LargeWidth, LargeHeight, inc, startline; uint8 S, Y_one, line; @@ -4065,7 +4065,10 @@ static void S9xDoDMA (void) /* Prepare for accessing $2118-2119 */ if (d->BAddress == 0x18 || d->BAddress == 0x19) { - FLUSH_REDRAW(); + if (IPPU.RenderThisFrame) + { + FLUSH_REDRAW(); + } } inc = d->AAddressFixed ? 0 : (!d->AAddressDecrement ? 1 : -1); @@ -5353,6 +5356,7 @@ void S9xSoftResetPPU (void) IPPU.ScreenColors[c] = c; IPPU.RenderedScreenWidth = SNES_WIDTH; IPPU.RenderedScreenHeight = SNES_HEIGHT; + IPPU.RenderThisFrame = TRUE; S9xFixColourBrightness(); diff --git a/src/ppu.h b/src/ppu.h index e1a352c..ec785fd 100644 --- a/src/ppu.h +++ b/src/ppu.h @@ -299,6 +299,7 @@ struct InternalPPU uint16 ScreenColors[256]; int RenderedScreenWidth; int RenderedScreenHeight; + bool8 RenderThisFrame; }; struct SOBJ diff --git a/src/snapshot.c b/src/snapshot.c index b9a8f18..dd7b7a7 100644 --- a/src/snapshot.c +++ b/src/snapshot.c @@ -1870,6 +1870,7 @@ int S9xUnfreezeFromStream (STREAM stream) CPU.HDMARanInDMA = 0; S9xFixColourBrightness(); + IPPU.RenderThisFrame = TRUE; IPPU.OBJChanged = TRUE; hdma_byte = Memory.FillRAM[0x420c]; diff --git a/src/snes9x.h b/src/snes9x.h index 49a9aa3..dceb5de 100644 --- a/src/snes9x.h +++ b/src/snes9x.h @@ -374,6 +374,7 @@ struct SSettings uint32 SoundPlaybackRate; uint32 SoundInputRate; + bool8 Mute; bool8 Multi; char CartAName[PATH_MAX + 1];