mirror of https://github.com/snes9xgit/snes9x.git
APU: Remove extra buffers.
This commit is contained in:
parent
8c9c3eb6a4
commit
e938225cd2
104
apu/apu.cpp
104
apu/apu.cpp
|
@ -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)
|
||||||
|
|
|
@ -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 )
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
29
msu1.cpp
29
msu1.cpp
|
@ -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
3
msu1.h
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue