BSNESv115+: Replace the 'snes_audio_sample' callback with a dynamic audio sample vector to reduce callbacks (#3010)

* BSNESv115+: Replace the 'snes_audio_sample' callback with a dynamic audio sample vector to reduce callbacks

* fix integration for subbsnes core

* reduce buffer allocations
- also make frame setter private now that it's possible
This commit is contained in:
Moritz Bender 2022-08-15 20:50:26 +02:00 committed by GitHub
parent f1e11dfc36
commit 039d822144
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 56 additions and 40 deletions

Binary file not shown.

View File

@ -24,6 +24,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
[BizImport(CallingConvention.Cdecl)]
public abstract void snes_set_hooks_enabled(bool readHookEnabled, bool writeHookEnabled, bool executeHookEnabled);
[BizImport(CallingConvention.Cdecl)]
public abstract short* snes_get_audiobuffer_and_size(out int size);
[BizImport(CallingConvention.Cdecl)]
public abstract BsnesApi.SNES_REGION snes_get_region();
[BizImport(CallingConvention.Cdecl)]
@ -167,7 +169,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
}
public delegate void snes_video_frame_t(ushort* data, int width, int height, int pitch);
public delegate void snes_audio_sample_t(short left, short right);
public delegate short snes_input_poll_t(int port, int index, int id);
public delegate void snes_controller_latch_t();
public delegate void snes_no_lag_t(bool sgb_poll);
@ -218,7 +219,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
public sealed class SnesCallbacks
{
public snes_video_frame_t videoFrameCb;
public snes_audio_sample_t audioSampleCb;
public snes_input_poll_t inputPollCb;
public snes_controller_latch_t controllerLatchCb;
public snes_no_lag_t noLagCb;

View File

@ -1,4 +1,7 @@
using BizHawk.Emulation.Common;
using System;
using System.Runtime.InteropServices;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.BSNES
{
@ -8,12 +11,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
public ControllerDefinition ControllerDefinition => _controllers.Definition;
private short[] _audioBuffer = Array.Empty<short>();
public bool FrameAdvance(IController controller, bool render, bool renderSound)
{
FrameAdvancePre(controller, render, renderSound);
IsLagFrame = true;
bool resetSignal = controller.IsPressed("Reset");
if (resetSignal)
{
@ -26,14 +29,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
Api.core.snes_power();
}
IsLagFrame = true;
// run the core for one frame
Api.core.snes_run(false);
Frame++;
if (IsLagFrame)
{
LagCount++;
}
FrameAdvancePost();
return true;
}
@ -65,7 +64,32 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
Api.core.snes_set_audio_enabled(renderSound);
}
public int Frame { get; set; }
internal void FrameAdvancePost()
{
int numSamples = UpdateAudioBuffer();
_soundProvider.PutSamples(_audioBuffer, numSamples / 2);
Frame++;
if (IsLagFrame)
{
LagCount++;
}
}
private unsafe int UpdateAudioBuffer()
{
using (Api.exe.EnterExit())
{
short* rawAudioBuffer = Api.core.snes_get_audiobuffer_and_size(out int size);
if (size > _audioBuffer.Length)
_audioBuffer = new short[size];
Marshal.Copy((IntPtr) rawAudioBuffer, _audioBuffer, 0, size);
return size;
}
}
public int Frame { get; private set; }
public string SystemId => VSystemID.Raw.SNES;
@ -86,7 +110,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
}
Api.Dispose();
_resampler.Dispose();
_currentMsuTrack?.Dispose();
_disposed = true;

View File

@ -2,6 +2,7 @@ using System;
using System.IO;
using BizHawk.Common;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Common.Base_Implementations;
using BizHawk.Emulation.Cores.Components.W65816;
// http://wiki.superfamicom.org/snes/show/Backgrounds
@ -47,7 +48,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
noLagCb = snes_no_lag,
controllerLatchCb = snes_controller_latch,
videoFrameCb = snes_video_refresh,
audioSampleCb = snes_audio_sample,
pathRequestCb = snes_path_request,
traceCb = snes_trace,
readHookCb = ReadHook,
@ -80,7 +80,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
// start up audio resampler
InitAudio();
ser.Register<ISoundProvider>(_resampler);
ser.Register<ISoundProvider>(_soundProvider);
if (IsSGB)
{
@ -124,7 +124,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
private readonly ITraceable _tracer;
private IController _controller;
private SpeexResampler _resampler;
private SimpleSyncSoundProvider _soundProvider;
private readonly string _romPath;
private bool _disposed;
@ -284,12 +284,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
private void InitAudio()
{
_resampler = new SpeexResampler(SpeexResampler.Quality.QUALITY_DESKTOP, 64080, 88200, 32040, 44100);
}
private void snes_audio_sample(short left, short right)
{
_resampler.EnqueueSample(left, right);
_soundProvider = new SimpleSyncSoundProvider();
}
private void snes_trace(string disassembly, string registerInfo)

View File

@ -80,12 +80,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
framePassed = _bsnesCore.Api.core.snes_run(subFrameRequested);
}
_bsnesCore.Frame++;
if (framePassed && _bsnesCore.IsLagFrame)
_bsnesCore.LagCount++;
else
_bsnesCore.IsLagFrame = false;
if (!framePassed) _bsnesCore.IsLagFrame = false;
_bsnesCore.FrameAdvancePost();
return true;
}

View File

@ -21,7 +21,7 @@ auto ICD::ppuWrite(uint2 color) -> void {
auto ICD::apuWrite(float left, float right) -> void {
double samples[] = {left, right};
if(!system.runAhead && system.renderAudio) stream->write(samples);
if(!system.runAhead && system.renderAudio && !scheduler.StepOnce) stream->write(samples);
}
auto ICD::joypWrite(bool p14, bool p15) -> void {

View File

@ -42,7 +42,7 @@ auto MSU1::main() -> void {
}
}
if(!system.runAhead && system.renderAudio) stream->sample(float(left), float(right));
if(!system.runAhead && system.renderAudio && !scheduler.StepOnce) stream->sample(float(left), float(right));
step(1);
synchronizeCPU();
}

View File

@ -18,7 +18,7 @@ auto DSP::main() -> void {
int count = spc_dsp.sample_count();
if(count > 0) {
if(!system.runAhead && system.renderAudio)
if(!system.runAhead && system.renderAudio && !scheduler.StepOnce)
for(uint n = 0; n < count; n += 2) {
float left = samplebuffer[n + 0] / 32768.0f;
float right = samplebuffer[n + 1] / 32768.0f;

View File

@ -146,8 +146,7 @@ EXPORT void snes_init(SnesInitData* init_data)
emulator->configure("Hacks/Coprocessor/DelayedSync", init_data->fast_coprocessors);
emulator->configure("Video/BlurEmulation", false); // blurs the video when not using fast ppu. I don't like it so I disable it here :)
// needed in order to get audio sync working. should probably figure out what exactly this does or how to change that properly
Emulator::audio.setFrequency(SAMPLE_RATE);
Emulator::audio.setFrequency(44100); // default is 48000, but bizhawk expects 44100
program->regionOverride = init_data->region_override;
}
@ -172,6 +171,7 @@ EXPORT void snes_reset(void)
EXPORT bool snes_run(bool breakOnLatch)
{
program->breakOnLatch = breakOnLatch;
audioBuffer.clear();
emulator->run();
return scheduler.event == Scheduler::Event::Frame;
}
@ -294,6 +294,11 @@ EXPORT System::Region snes_get_region(void) {
return SuperFamicom::system.region();
}
EXPORT short* snes_get_audiobuffer_and_size(int& out_size) {
out_size = audioBuffer.size();
return audioBuffer.data();
}
EXPORT char snes_get_mapper(void) {
string board = program->superFamicom.document["game/board"].text();
string mapper = board.split('-', 1)[0];

View File

@ -6,8 +6,6 @@
#define EXPORT ECL_EXPORT
#define SAMPLE_RATE 32040
enum SNES_MEMORY {
CARTRIDGE_RAM,
CARTRIDGE_ROM,

View File

@ -4,7 +4,6 @@
#include <stdint.h>
typedef void (*snes_video_frame_t)(const uint16_t* data, int width, int height, int pitch);
typedef void (*snes_audio_sample_t)(int16_t left, int16_t right);
typedef int16_t (*snes_input_poll_t)(int port, int index, int id);
typedef void (*snes_controller_latch_t)(void);
typedef void (*snes_no_lag_t)(bool sgb_poll);
@ -20,7 +19,6 @@ typedef bool (*snes_msu_end_t)(void);
struct SnesCallbacks {
snes_video_frame_t snes_video_frame;
snes_audio_sample_t snes_audio_sample;
snes_input_poll_t snes_input_poll;
snes_controller_latch_t snes_controller_latch;
snes_no_lag_t snes_no_lag;

View File

@ -8,8 +8,10 @@
#include "resources.hpp"
#include <nall/vfs/biz_file.hpp>
#include <vector>
static Emulator::Interface *emulator;
static std::vector<short> audioBuffer;
struct Program : Emulator::Platform
{
@ -453,9 +455,8 @@ static int16_t d2i16(double v)
auto Program::audioFrame(const double* samples, uint channels) -> void
{
int16_t left = d2i16(samples[0]);
int16_t right = d2i16(samples[1]);
return snesCallbacks.snes_audio_sample(left, right);
audioBuffer.push_back(d2i16(samples[0]));
audioBuffer.push_back(d2i16(samples[1]));
}
auto Program::notify(string message) -> void