diff --git a/apu/apu.cpp b/apu/apu.cpp index 6479d041..6d119582 100644 --- a/apu/apu.cpp +++ b/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_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. // This is 535 sample frames, which corresponds to 1 video frame + some leeway -// for use with SoundSync. -static const int MAX_SAMPLE_FRAMES = 600; -static const int SAMPLE_FRAMES_LIMIT = 550; +// for use with SoundSync, multiplied by 2, for left and right samples. +static const int MINIMUM_BUFFER_SIZE = 550 * 2; namespace SNES { @@ -42,7 +41,6 @@ static void *callback_data = NULL; static bool8 sound_in_sync = TRUE; static bool8 sound_enabled = FALSE; -static uint16 dsp_buffer[MAX_SAMPLE_FRAMES * 2]; static Resampler *resampler = NULL; @@ -62,10 +60,8 @@ static double dynamic_rate_multiplier = 1.0; namespace msu { // 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 std::vector resample_buffer; +static std::vector resample_buffer; } // namespace msu static void UpdatePlaybackRate(void); @@ -73,12 +69,6 @@ static void SPCSnapshotCallback(void); static inline int S9xAPUGetClock(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) { int16 *out = (int16 *)dest; @@ -86,10 +76,7 @@ bool8 S9xMixSamples(uint8 *dest, int sample_count) if (Settings.Mute) { memset(out, 0, sample_count << 1); - spc::resampler->clear(); - - if (Settings.MSU1) - msu::resampler->clear(); + S9xClearSamples(); } 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; } @@ -129,57 +121,17 @@ int S9xGetSampleCount(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) { if (spc::callback != NULL) spc::callback(spc::callback_data); + + if (spc::resampler->space_empty() >= 535 * 2) + spc::sound_in_sync = true; else - S9xFinalizeSamples(); + spc::sound_in_sync = false; } void S9xClearSamples(void) @@ -237,9 +189,11 @@ static void UpdatePlaybackRate(void) bool8 S9xInitSound(int buffer_ms, int unused2) { // The resampler and spc unit use samples (16-bit short) as arguments. - int buffer_size_samples = MAX_SAMPLE_FRAMES * 2; - if (buffer_ms > 0) - buffer_size_samples = Settings.SoundPlaybackRate * buffer_ms * 2 / 1000; + int buffer_size_samples = MINIMUM_BUFFER_SIZE; + int requested_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) { @@ -247,6 +201,9 @@ bool8 S9xInitSound(int buffer_ms, int unused2) if (!spc::resampler) return (FALSE); } + else + spc::resampler->resize(buffer_size_samples); + if (!msu::resampler) { @@ -257,7 +214,8 @@ bool8 S9xInitSound(int buffer_ms, int unused2) else msu::resampler->resize(buffer_size_samples * 3 / 2); - reset_dsp_output(); + SNES::dsp.spc_dsp.set_output(spc::resampler); + S9xMSU1SetOutput(msu::resampler); UpdatePlaybackRate(); @@ -358,7 +316,7 @@ void S9xAPUEndScanline(void) S9xAPUExecute(); 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(); } @@ -385,13 +343,9 @@ void S9xResetAPU(void) SNES::cpu.frequency = Settings.PAL ? PAL_MASTER_CLOCK : NTSC_MASTER_CLOCK; SNES::smp.power(); SNES::dsp.power(); - reset_dsp_output(); SNES::dsp.spc_dsp.set_spc_snapshot_callback(SPCSnapshotCallback); - spc::resampler->clear(); - - if (Settings.MSU1) - msu::resampler->clear(); + S9xClearSamples(); } void S9xSoftResetAPU(void) @@ -401,12 +355,8 @@ void S9xSoftResetAPU(void) SNES::cpu.reset(); SNES::smp.reset(); SNES::dsp.reset(); - reset_dsp_output(); - spc::resampler->clear(); - - if (Settings.MSU1) - msu::resampler->clear(); + S9xClearSamples(); } void S9xAPUSaveState(uint8 *block) diff --git a/apu/bapu/dsp/SPC_DSP.cpp b/apu/bapu/dsp/SPC_DSP.cpp index a6f19a59..cc649467 100644 --- a/apu/bapu/dsp/SPC_DSP.cpp +++ b/apu/bapu/dsp/SPC_DSP.cpp @@ -64,7 +64,19 @@ static BOOST::uint8_t const initial_regs [SPC_DSP::register_count] = out [0] = l;\ out [1] = r;\ 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 ) { diff --git a/apu/bapu/dsp/SPC_DSP.h b/apu/bapu/dsp/SPC_DSP.h index ca61cc95..8b9d8173 100644 --- a/apu/bapu/dsp/SPC_DSP.h +++ b/apu/bapu/dsp/SPC_DSP.h @@ -22,6 +22,8 @@ public: typedef short sample_t; 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 // a multiple of 2. Undefined if more samples were generated than // output buffer could hold. @@ -135,6 +137,8 @@ public: private: enum { brr_block_size = 9 }; + Resampler *resampler; + struct state_t { uint8_t regs [register_count]; @@ -193,6 +197,7 @@ private: // non-emulation state uint8_t* ram; // 64K shared RAM between DSP and SMP int mute_mask; + sample_t* out; sample_t* out_end; sample_t* out_begin; diff --git a/apu/bapu/snes/snes.hpp b/apu/bapu/snes/snes.hpp index a86fd176..d85e99f0 100644 --- a/apu/bapu/snes/snes.hpp +++ b/apu/bapu/snes/snes.hpp @@ -2,6 +2,8 @@ #define __SNES_HPP #include "../../../snes9x.h" +#include "../../resampler.h" +#include "../../../msu1.h" #define SNES9X diff --git a/apu/resampler.h b/apu/resampler.h index a888f591..147b5db5 100644 --- a/apu/resampler.h +++ b/apu/resampler.h @@ -23,7 +23,7 @@ struct Resampler float r_frac; 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); } diff --git a/msu1.cpp b/msu1.cpp index 1ba77d6a..75123e94 100644 --- a/msu1.cpp +++ b/msu1.cpp @@ -18,7 +18,7 @@ uint32 audioLoopPos; size_t partial_frames; // Sample buffer -int16 *bufPos, *bufBegin, *bufEnd; +static Resampler *msu_resampler = NULL; #ifdef UNZIP_SUPPORT 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_RESUME_POS = 0; - - bufPos = 0; - bufBegin = 0; - bufEnd = 0; + if (msu_resampler) + msu_resampler->clear(); partial_frames = 0; @@ -231,7 +229,7 @@ void S9xMSU1Generate(size_t sample_count) { partial_frames += 4410 * (sample_count / 2); - while ((bufPos < (bufEnd - 2)) && partial_frames >= 3204) + while (partial_frames >= 3204) { 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); *right = ((int32)(int16)GET_LE16(right) * MSU1.MSU1_VOLUME / 255); - - *(bufPos++) = *left; - *(bufPos++) = *right; + msu_resampler->push_sample(*left, *right); MSU1.MSU1_AUDIO_POS += 4; partial_frames -= 3204; } @@ -274,8 +270,7 @@ void S9xMSU1Generate(size_t sample_count) { MSU1.MSU1_STATUS &= ~(AudioPlaying | AudioRepeating); partial_frames -= 3204; - *(bufPos++) = 0; - *(bufPos++) = 0; + msu_resampler->push_sample(0, 0); } } } @@ -392,13 +387,12 @@ void S9xMSU1WritePort(uint8 port, uint8 byte) 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; - bufEnd = out + size; + msu_resampler = resampler; } void S9xMSU1PostLoadState(void) @@ -427,9 +421,8 @@ void S9xMSU1PostLoadState(void) } } - bufPos = 0; - bufBegin = 0; - bufEnd = 0; + if (msu_resampler) + msu_resampler->clear(); partial_frames = 0; } diff --git a/msu1.h b/msu1.h index 8c9c29df..7ffc5310 100644 --- a/msu1.h +++ b/msu1.h @@ -7,6 +7,7 @@ #ifndef _MSU1_H_ #define _MSU1_H_ #include "snes9x.h" +#include "apu/resampler.h" #define MSU1_REVISION 0x02 @@ -51,7 +52,7 @@ void S9xMSU1Generate(size_t sample_count); uint8 S9xMSU1ReadPort(uint8 port); void S9xMSU1WritePort(uint8 port, uint8 byte); size_t S9xMSU1Samples(void); -void S9xMSU1SetOutput(int16 *out, size_t size); +void S9xMSU1SetOutput(Resampler *resampler); void S9xMSU1PostLoadState(void); #endif