APU: Remove extra buffers.

This commit is contained in:
Brandon Wright 2019-02-09 12:03:34 -06:00
parent 8c9c3eb6a4
commit e938225cd2
7 changed files with 61 additions and 98 deletions

View File

@ -22,12 +22,11 @@ static const int APU_DENOMINATOR_NTSC = 328125;
static const int APU_NUMERATOR_PAL = 34176; static const int APU_NUMERATOR_PAL = 34176;
static const int APU_DENOMINATOR_PAL = 709379; static const int APU_DENOMINATOR_PAL = 709379;
// Max number of sample frames we'll ever generate before call to port API and // Max number of samples we'll ever generate before call to port API and
// moving the samples to the resampler. // moving the samples to the resampler.
// This is 535 sample frames, which corresponds to 1 video frame + some leeway // This is 535 sample frames, which corresponds to 1 video frame + some leeway
// for use with SoundSync. // for use with SoundSync, multiplied by 2, for left and right samples.
static const int MAX_SAMPLE_FRAMES = 600; static const int MINIMUM_BUFFER_SIZE = 550 * 2;
static const int SAMPLE_FRAMES_LIMIT = 550;
namespace SNES namespace SNES
{ {
@ -42,7 +41,6 @@ static void *callback_data = NULL;
static bool8 sound_in_sync = TRUE; static bool8 sound_in_sync = TRUE;
static bool8 sound_enabled = FALSE; static bool8 sound_enabled = FALSE;
static uint16 dsp_buffer[MAX_SAMPLE_FRAMES * 2];
static Resampler *resampler = NULL; static Resampler *resampler = NULL;
@ -62,10 +60,8 @@ static double dynamic_rate_multiplier = 1.0;
namespace msu namespace msu
{ {
// Always 16-bit, Stereo; 1.5x dsp buffer to never overflow // Always 16-bit, Stereo; 1.5x dsp buffer to never overflow
static const int buffer_size = MAX_SAMPLE_FRAMES * 6;
static uint8 mixing_buffer[buffer_size];
static Resampler *resampler = NULL; static Resampler *resampler = NULL;
static std::vector<uint16> resample_buffer; static std::vector<int16> resample_buffer;
} // namespace msu } // namespace msu
static void UpdatePlaybackRate(void); static void UpdatePlaybackRate(void);
@ -73,12 +69,6 @@ static void SPCSnapshotCallback(void);
static inline int S9xAPUGetClock(int32); static inline int S9xAPUGetClock(int32);
static inline int S9xAPUGetClockRemainder(int32); static inline int S9xAPUGetClockRemainder(int32);
static void reset_dsp_output()
{
SNES::dsp.spc_dsp.set_output((SNES::SPC_DSP::sample_t *)spc::dsp_buffer,
MAX_SAMPLE_FRAMES * 2);
}
bool8 S9xMixSamples(uint8 *dest, int sample_count) bool8 S9xMixSamples(uint8 *dest, int sample_count)
{ {
int16 *out = (int16 *)dest; int16 *out = (int16 *)dest;
@ -86,10 +76,7 @@ bool8 S9xMixSamples(uint8 *dest, int sample_count)
if (Settings.Mute) if (Settings.Mute)
{ {
memset(out, 0, sample_count << 1); memset(out, 0, sample_count << 1);
spc::resampler->clear(); S9xClearSamples();
if (Settings.MSU1)
msu::resampler->clear();
} }
else else
{ {
@ -119,6 +106,11 @@ bool8 S9xMixSamples(uint8 *dest, int sample_count)
} }
} }
if (spc::resampler->space_empty() >= 535 * 2)
spc::sound_in_sync = true;
else
spc::sound_in_sync = false;
return true; return true;
} }
@ -129,57 +121,17 @@ int S9xGetSampleCount(void)
void S9xFinalizeSamples(void) void S9xFinalizeSamples(void)
{ {
if (!Settings.Mute)
{
if (!spc::resampler->push((short *)spc::dsp_buffer,
SNES::dsp.spc_dsp.sample_count()))
{
// Don't drop samples if SoundSync is enabled. The port will wait
// and call S9xSyncSound again to catch up.
// If the sample count is over a frame, it indicates the port
// didn't implement SoundSync correctly.
if (Settings.SoundSync && !Settings.TurboMode && !Settings.Mute &&
SNES::dsp.spc_dsp.sample_count() < SAMPLE_FRAMES_LIMIT)
{
spc::sound_in_sync = false;
return;
}
spc::resampler->clear();
spc::resampler->push((short *)spc::dsp_buffer,
SNES::dsp.spc_dsp.sample_count());
if (Settings.MSU1)
msu::resampler->clear();
}
}
// only generate msu1 if we really consumed the dsp samples (sample_count() resets at end of function),
// otherwise we will generate multiple times for the same samples - so this needs to be after all early
// function returns
if (Settings.MSU1)
{
// generate the same number of msu1 samples as dsp samples were generated
S9xMSU1SetOutput((int16 *)msu::mixing_buffer, msu::buffer_size);
S9xMSU1Generate(SNES::dsp.spc_dsp.sample_count());
if (!msu::resampler->push((short *)msu::mixing_buffer, S9xMSU1Samples()))
{
// should not occur, msu buffer is larger and we drop msu samples if spc buffer overruns
assert(0);
}
}
spc::sound_in_sync = true;
reset_dsp_output();
} }
void S9xLandSamples(void) void S9xLandSamples(void)
{ {
if (spc::callback != NULL) if (spc::callback != NULL)
spc::callback(spc::callback_data); spc::callback(spc::callback_data);
if (spc::resampler->space_empty() >= 535 * 2)
spc::sound_in_sync = true;
else else
S9xFinalizeSamples(); spc::sound_in_sync = false;
} }
void S9xClearSamples(void) void S9xClearSamples(void)
@ -237,9 +189,11 @@ static void UpdatePlaybackRate(void)
bool8 S9xInitSound(int buffer_ms, int unused2) bool8 S9xInitSound(int buffer_ms, int unused2)
{ {
// The resampler and spc unit use samples (16-bit short) as arguments. // The resampler and spc unit use samples (16-bit short) as arguments.
int buffer_size_samples = MAX_SAMPLE_FRAMES * 2; int buffer_size_samples = MINIMUM_BUFFER_SIZE;
if (buffer_ms > 0) int requested_buffer_size_samples = Settings.SoundPlaybackRate * buffer_ms * 2 / 1000;
buffer_size_samples = Settings.SoundPlaybackRate * buffer_ms * 2 / 1000;
if (requested_buffer_size_samples > buffer_size_samples)
buffer_size_samples = requested_buffer_size_samples;
if (!spc::resampler) if (!spc::resampler)
{ {
@ -247,6 +201,9 @@ bool8 S9xInitSound(int buffer_ms, int unused2)
if (!spc::resampler) if (!spc::resampler)
return (FALSE); return (FALSE);
} }
else
spc::resampler->resize(buffer_size_samples);
if (!msu::resampler) if (!msu::resampler)
{ {
@ -257,7 +214,8 @@ bool8 S9xInitSound(int buffer_ms, int unused2)
else else
msu::resampler->resize(buffer_size_samples * 3 / 2); msu::resampler->resize(buffer_size_samples * 3 / 2);
reset_dsp_output(); SNES::dsp.spc_dsp.set_output(spc::resampler);
S9xMSU1SetOutput(msu::resampler);
UpdatePlaybackRate(); UpdatePlaybackRate();
@ -358,7 +316,7 @@ void S9xAPUEndScanline(void)
S9xAPUExecute(); S9xAPUExecute();
SNES::dsp.synchronize(); SNES::dsp.synchronize();
if (SNES::dsp.spc_dsp.sample_count() >= APU_SAMPLE_BLOCK || !spc::sound_in_sync) if (spc::resampler->space_filled() >= APU_SAMPLE_BLOCK || !spc::sound_in_sync)
S9xLandSamples(); S9xLandSamples();
} }
@ -385,13 +343,9 @@ void S9xResetAPU(void)
SNES::cpu.frequency = Settings.PAL ? PAL_MASTER_CLOCK : NTSC_MASTER_CLOCK; SNES::cpu.frequency = Settings.PAL ? PAL_MASTER_CLOCK : NTSC_MASTER_CLOCK;
SNES::smp.power(); SNES::smp.power();
SNES::dsp.power(); SNES::dsp.power();
reset_dsp_output();
SNES::dsp.spc_dsp.set_spc_snapshot_callback(SPCSnapshotCallback); SNES::dsp.spc_dsp.set_spc_snapshot_callback(SPCSnapshotCallback);
spc::resampler->clear(); S9xClearSamples();
if (Settings.MSU1)
msu::resampler->clear();
} }
void S9xSoftResetAPU(void) void S9xSoftResetAPU(void)
@ -401,12 +355,8 @@ void S9xSoftResetAPU(void)
SNES::cpu.reset(); SNES::cpu.reset();
SNES::smp.reset(); SNES::smp.reset();
SNES::dsp.reset(); SNES::dsp.reset();
reset_dsp_output();
spc::resampler->clear(); S9xClearSamples();
if (Settings.MSU1)
msu::resampler->clear();
} }
void S9xAPUSaveState(uint8 *block) void S9xAPUSaveState(uint8 *block)

View File

@ -64,7 +64,19 @@ static BOOST::uint8_t const initial_regs [SPC_DSP::register_count] =
out [0] = l;\ out [0] = l;\
out [1] = r;\ out [1] = r;\
out += 2;\ out += 2;\
}\ }
#define SPC_DSP_OUT_HOOK(l, r) \
{ \
resampler->push_sample(l, r); \
if (Settings.MSU1) \
S9xMSU1Generate(2); \
}
void SPC_DSP::set_output( Resampler *resampler )
{
this->resampler = resampler;
}
void SPC_DSP::set_output( sample_t* out, int size ) void SPC_DSP::set_output( sample_t* out, int size )
{ {

View File

@ -22,6 +22,8 @@ public:
typedef short sample_t; typedef short sample_t;
void set_output( sample_t* out, int out_size ); void set_output( sample_t* out, int out_size );
void set_output( Resampler* resampler );
// Number of samples written to output since it was last set, always // Number of samples written to output since it was last set, always
// a multiple of 2. Undefined if more samples were generated than // a multiple of 2. Undefined if more samples were generated than
// output buffer could hold. // output buffer could hold.
@ -135,6 +137,8 @@ public:
private: private:
enum { brr_block_size = 9 }; enum { brr_block_size = 9 };
Resampler *resampler;
struct state_t struct state_t
{ {
uint8_t regs [register_count]; uint8_t regs [register_count];
@ -193,6 +197,7 @@ private:
// non-emulation state // non-emulation state
uint8_t* ram; // 64K shared RAM between DSP and SMP uint8_t* ram; // 64K shared RAM between DSP and SMP
int mute_mask; int mute_mask;
sample_t* out; sample_t* out;
sample_t* out_end; sample_t* out_end;
sample_t* out_begin; sample_t* out_begin;

View File

@ -2,6 +2,8 @@
#define __SNES_HPP #define __SNES_HPP
#include "../../../snes9x.h" #include "../../../snes9x.h"
#include "../../resampler.h"
#include "../../../msu1.h"
#define SNES9X #define SNES9X

View File

@ -23,7 +23,7 @@ struct Resampler
float r_frac; float r_frac;
int r_left[4], r_right[4]; int r_left[4], r_right[4];
static inline int16_t short_clamp(int16_t n) static inline int16_t short_clamp(int n)
{ {
return (int16_t)(((int16_t)n != n) ? (n >> 31) ^ 0x7fff : n); return (int16_t)(((int16_t)n != n) ? (n >> 31) ^ 0x7fff : n);
} }

View File

@ -18,7 +18,7 @@ uint32 audioLoopPos;
size_t partial_frames; size_t partial_frames;
// Sample buffer // Sample buffer
int16 *bufPos, *bufBegin, *bufEnd; static Resampler *msu_resampler = NULL;
#ifdef UNZIP_SUPPORT #ifdef UNZIP_SUPPORT
static int unzFindExtension(unzFile &file, const char *ext, bool restart = TRUE, bool print = TRUE, bool allowExact = FALSE) static int unzFindExtension(unzFile &file, const char *ext, bool restart = TRUE, bool print = TRUE, bool allowExact = FALSE)
@ -174,10 +174,8 @@ void S9xResetMSU(void)
MSU1.MSU1_AUDIO_POS = 0; MSU1.MSU1_AUDIO_POS = 0;
MSU1.MSU1_RESUME_POS = 0; MSU1.MSU1_RESUME_POS = 0;
if (msu_resampler)
bufPos = 0; msu_resampler->clear();
bufBegin = 0;
bufEnd = 0;
partial_frames = 0; partial_frames = 0;
@ -231,7 +229,7 @@ void S9xMSU1Generate(size_t sample_count)
{ {
partial_frames += 4410 * (sample_count / 2); partial_frames += 4410 * (sample_count / 2);
while ((bufPos < (bufEnd - 2)) && partial_frames >= 3204) while (partial_frames >= 3204)
{ {
if (MSU1.MSU1_STATUS & AudioPlaying && audioStream) if (MSU1.MSU1_STATUS & AudioPlaying && audioStream)
{ {
@ -245,9 +243,7 @@ void S9xMSU1Generate(size_t sample_count)
*left = ((int32)(int16)GET_LE16(left) * MSU1.MSU1_VOLUME / 255); *left = ((int32)(int16)GET_LE16(left) * MSU1.MSU1_VOLUME / 255);
*right = ((int32)(int16)GET_LE16(right) * MSU1.MSU1_VOLUME / 255); *right = ((int32)(int16)GET_LE16(right) * MSU1.MSU1_VOLUME / 255);
msu_resampler->push_sample(*left, *right);
*(bufPos++) = *left;
*(bufPos++) = *right;
MSU1.MSU1_AUDIO_POS += 4; MSU1.MSU1_AUDIO_POS += 4;
partial_frames -= 3204; partial_frames -= 3204;
} }
@ -274,8 +270,7 @@ void S9xMSU1Generate(size_t sample_count)
{ {
MSU1.MSU1_STATUS &= ~(AudioPlaying | AudioRepeating); MSU1.MSU1_STATUS &= ~(AudioPlaying | AudioRepeating);
partial_frames -= 3204; partial_frames -= 3204;
*(bufPos++) = 0; msu_resampler->push_sample(0, 0);
*(bufPos++) = 0;
} }
} }
} }
@ -392,13 +387,12 @@ void S9xMSU1WritePort(uint8 port, uint8 byte)
size_t S9xMSU1Samples(void) size_t S9xMSU1Samples(void)
{ {
return bufPos - bufBegin; return msu_resampler->space_filled();
} }
void S9xMSU1SetOutput(int16 * out, size_t size) void S9xMSU1SetOutput(Resampler *resampler)
{ {
bufPos = bufBegin = out; msu_resampler = resampler;
bufEnd = out + size;
} }
void S9xMSU1PostLoadState(void) void S9xMSU1PostLoadState(void)
@ -427,9 +421,8 @@ void S9xMSU1PostLoadState(void)
} }
} }
bufPos = 0; if (msu_resampler)
bufBegin = 0; msu_resampler->clear();
bufEnd = 0;
partial_frames = 0; partial_frames = 0;
} }

3
msu1.h
View File

@ -7,6 +7,7 @@
#ifndef _MSU1_H_ #ifndef _MSU1_H_
#define _MSU1_H_ #define _MSU1_H_
#include "snes9x.h" #include "snes9x.h"
#include "apu/resampler.h"
#define MSU1_REVISION 0x02 #define MSU1_REVISION 0x02
@ -51,7 +52,7 @@ void S9xMSU1Generate(size_t sample_count);
uint8 S9xMSU1ReadPort(uint8 port); uint8 S9xMSU1ReadPort(uint8 port);
void S9xMSU1WritePort(uint8 port, uint8 byte); void S9xMSU1WritePort(uint8 port, uint8 byte);
size_t S9xMSU1Samples(void); size_t S9xMSU1Samples(void);
void S9xMSU1SetOutput(int16 *out, size_t size); void S9xMSU1SetOutput(Resampler *resampler);
void S9xMSU1PostLoadState(void); void S9xMSU1PostLoadState(void);
#endif #endif