SubGambatte (squashed PR #2732)

This commit is contained in:
CasualPokePlayer 2021-06-07 02:18:24 -07:00 committed by GitHub
parent b62f4bc6a9
commit e5e187982a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 89 additions and 41 deletions

View File

@ -9,17 +9,36 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{ {
public IEmulatorServiceProvider ServiceProvider { get; } public IEmulatorServiceProvider ServiceProvider { get; }
public ControllerDefinition ControllerDefinition => GbController; public ControllerDefinition ControllerDefinition => (_syncSettings.FrameLength == GambatteSyncSettings.FrameLengthType.UserDefinedFrames) ? SubGbController : GbController;
public bool FrameAdvance(IController controller, bool render, bool rendersound) public bool FrameAdvance(IController controller, bool render, bool rendersound)
{ {
FrameAdvancePrep(controller); FrameAdvancePrep(controller);
if (_syncSettings.EqualLengthFrames) uint samplesEmitted;
switch (_syncSettings.FrameLength)
{ {
case GambatteSyncSettings.FrameLengthType.VBlankDrivenFrames:
// target number of samples to emit: always 59.7fps
// runfor() always ends after creating a video frame, so sync-up is guaranteed
// when the display has been off, some frames can be markedly shorter than expected
samplesEmitted = TICKSINFRAME;
if (LibGambatte.gambatte_runfor(GambatteState, _soundbuff, ref samplesEmitted) > 0)
{
LibGambatte.gambatte_blitto(GambatteState, VideoBuffer, 160);
}
_cycleCount += samplesEmitted;
frameOverflow = 0;
if (rendersound && !Muted)
{
ProcessSound((int)samplesEmitted);
}
break;
case GambatteSyncSettings.FrameLengthType.EqualLengthFrames:
while (true) while (true)
{ {
// target number of samples to emit: length of 1 frame minus whatever overflow // target number of samples to emit: length of 1 frame minus whatever overflow
uint samplesEmitted = TICKSINFRAME - frameOverflow; samplesEmitted = TICKSINFRAME - frameOverflow;
Debug.Assert(samplesEmitted * 2 <= _soundbuff.Length); Debug.Assert(samplesEmitted * 2 <= _soundbuff.Length);
if (LibGambatte.gambatte_runfor(GambatteState, _soundbuff, ref samplesEmitted) > 0) if (LibGambatte.gambatte_runfor(GambatteState, _soundbuff, ref samplesEmitted) > 0)
{ {
@ -41,24 +60,40 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
break; break;
} }
} }
} break;
else case GambatteSyncSettings.FrameLengthType.UserDefinedFrames:
while (true)
{ {
// target number of samples to emit: always 59.7fps // target number of samples to emit: input length minus whatever overflow
// runfor() always ends after creating a video frame, so sync-up is guaranteed float inputFrameLength = controller.AxisValue("Input Length");
// when the display has been off, some frames can be markedly shorter than expected uint inputFrameLengthInt = (uint)Math.Floor(inputFrameLength);
uint samplesEmitted = TICKSINFRAME; if (inputFrameLengthInt == 0)
{
inputFrameLengthInt = TICKSINFRAME;
}
samplesEmitted = inputFrameLengthInt - frameOverflow;
Debug.Assert(samplesEmitted * 2 <= _soundbuff.Length);
if (LibGambatte.gambatte_runfor(GambatteState, _soundbuff, ref samplesEmitted) > 0) if (LibGambatte.gambatte_runfor(GambatteState, _soundbuff, ref samplesEmitted) > 0)
{ {
LibGambatte.gambatte_blitto(GambatteState, VideoBuffer, 160); LibGambatte.gambatte_blitto(GambatteState, VideoBuffer, 160);
} }
// account for actual number of samples emitted
_cycleCount += samplesEmitted; _cycleCount += samplesEmitted;
frameOverflow = 0; frameOverflow += samplesEmitted;
if (rendersound && !Muted) if (rendersound && !Muted)
{ {
ProcessSound((int)samplesEmitted); ProcessSound((int)samplesEmitted);
} }
if (frameOverflow >= inputFrameLengthInt)
{
frameOverflow -= inputFrameLengthInt;
break;
}
}
break;
} }
if (rendersound && !Muted) if (rendersound && !Muted)

View File

@ -256,15 +256,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
set => _latchedRTCSeconds = Math.Max(0, Math.Min(63, value)); set => _latchedRTCSeconds = Math.Max(0, Math.Min(63, value));
} }
[DisplayName("Equal Length Frames")] public enum FrameLengthType
[Description("When false, emulation frames sync to vblank. Only useful for high level TASing.")]
[DefaultValue(false)]
public bool EqualLengthFrames
{ {
get => _equalLengthFrames; VBlankDrivenFrames,
set => _equalLengthFrames = value; EqualLengthFrames,
UserDefinedFrames
} }
[DisplayName("Frame Length")]
[Description("Sets how long an emulation frame will last.\nVBlank Driven Frames will make emulation frames sync to VBlank. Recommended for TASing.\nEqual Length Frames will force all frames to emit 35112 samples. Legacy, not recommended for TASing.\nUser Defined Frames allows for the user to define how many samples are emitted for each frame. Only useful if sub-frame input is desired.")]
[DefaultValue(FrameLengthType.VBlankDrivenFrames)]
public FrameLengthType FrameLength { get; set; }
[DisplayName("Display BG")] [DisplayName("Display BG")]
[Description("Display background")] [Description("Display background")]
[DefaultValue(true)] [DefaultValue(true)]
@ -282,7 +285,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
[JsonIgnore] [JsonIgnore]
[DeepEqualsIgnore] [DeepEqualsIgnore]
private bool _equalLengthFrames; private FrameLengthType _frameLength;
public GambatteSyncSettings() public GambatteSyncSettings()
{ {

View File

@ -1,5 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using BizHawk.Common;
using BizHawk.Common.BufferExtensions; using BizHawk.Common.BufferExtensions;
using BizHawk.Emulation.Common; using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy; using BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy;
@ -247,6 +248,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
} }
}; };
public static readonly ControllerDefinition SubGbController = new ControllerDefinition
{
Name = "Subframe Gameboy Controller",
BoolButtons =
{
"Up", "Down", "Left", "Right", "Start", "Select", "B", "A", "Power"
}
}.AddAxis("Input Length", 0.RangeTo(35112), 35112);
private LibGambatte.Buttons ControllerCallback() private LibGambatte.Buttons ControllerCallback()
{ {
InputCallbacks.Call(); InputCallbacks.Call();