diff options
author | Nebuleon Fumika | 2013-01-26 14:49:18 -0500 |
---|---|---|
committer | Nebuleon Fumika | 2013-01-26 14:49:18 -0500 |
commit | 2b715684087675747df7cb8995695936f20c782b (patch) | |
tree | 3ed587f38ed2cbb2aff474be8d372a6417e4ed2b /source | |
parent | 2b9a9dc05b5c4abdf5dac5ceba9300734d4f1b65 (diff) | |
download | snes9x2005-2b715684087675747df7cb8995695936f20c782b.tar.gz snes9x2005-2b715684087675747df7cb8995695936f20c782b.tar.bz2 snes9x2005-2b715684087675747df7cb8995695936f20c782b.zip |
Reverse the loop in MixStereo such that instead of "for each channel, for each playback sample, add into the mix buffer", the playback sample is instead written to the mix buffer only when fully mixed from the 8 SNES sound channels.
Verified working with Secret of Mana, Super Bomberman 5 and Super Mario World.
Diffstat (limited to 'source')
-rw-r--r-- | source/soundux.cpp | 438 | ||||
-rw-r--r-- | source/soundux.h | 2 |
2 files changed, 386 insertions, 54 deletions
diff --git a/source/soundux.cpp b/source/soundux.cpp index 5e7f865..1c05e77 100644 --- a/source/soundux.cpp +++ b/source/soundux.cpp @@ -131,18 +131,20 @@ #include "memmap.h" #include "cpuexec.h" -extern int Echo [24000]; -extern int DummyEchoBuffer [SOUND_BUFFER_SIZE]; -extern int MixBuffer [SOUND_BUFFER_SIZE]; -extern int EchoBuffer [SOUND_BUFFER_SIZE]; -extern int FilterTaps [8]; +extern int32 Echo [24000]; +extern int32 DummyEchoBuffer [SOUND_BUFFER_SIZE]; +extern int32 MixBuffer [SOUND_BUFFER_SIZE]; +extern int32 EchoBuffer [SOUND_BUFFER_SIZE]; +extern int32 FilterTaps [8]; +static uint8 FilterTapDefinitionBitfield; +// In the above, bit I is set if FilterTaps[I] is non-zero. extern unsigned long Z; -extern int Loop [16]; +extern int32 Loop [16]; extern long FilterValues[4][2]; -extern int NoiseFreq [32]; +extern int32 NoiseFreq [32]; -static int noise_gen; +static int32 noise_gen; #undef ABS #define ABS(a) ((a) < 0 ? -(a) : (a)) @@ -402,15 +404,11 @@ void S9xFixSoundAfterSnapshotLoad () void S9xSetFilterCoefficient (int tap, int value) { - FilterTaps [tap & 7] = value; - SoundData.no_filter = (FilterTaps [0] == 127 || FilterTaps [0] == 0) && - FilterTaps [1] == 0 && - FilterTaps [2] == 0 && - FilterTaps [3] == 0 && - FilterTaps [4] == 0 && - FilterTaps [5] == 0 && - FilterTaps [6] == 0 && - FilterTaps [7] == 0; + FilterTaps [tap & 7] = value; + if (value == 0 || (tap == 0 && value == 127)) + FilterTapDefinitionBitfield &= ~(1 << (tap & 7)); + else + FilterTapDefinitionBitfield |= 1 << (tap & 7); } void S9xSetSoundADSR (int channel, int attack_rate, int decay_rate, @@ -891,10 +889,337 @@ void DecodeBlock (Channel *ch) ch->block_pointer += 9; } +/* + * The layout of loops in this function is optimised for outputting a few + * samples, reducing the number of memory accesses per sample-channel pair. + * For each SAMPLE, each channel is mixed into CPU registers and + * assigned to the mix buffer. + */ +static inline void MixStereo_ReversedLoop (int sample_count) +{ + int pitch_mod = SoundData.pitch_mod & ~APU.DSP[APU_NON]; + int32 VL[8], VR[8]; + int32 PreviousChannelSample; + + for (uint32 I = 0; I < (uint32) sample_count; I += 2) + { + int32 OutputLeft = 0, OutputRight = 0; + + for (uint32 J = 0; J < NUM_CHANNELS; J++) + { + Channel *ch = &SoundData.channels[J]; + + if (ch->state == SOUND_SILENT || !(so.sound_switch & (1 << J))) + continue; + + unsigned long freq0 = ch->frequency; + + // freq0 = (unsigned long) ((double) freq0 * 0.985);//uncommented by jonathan gevaryahu, as it is necessary for most cards in linux + freq0 = freq0 * 985/1000; + + bool8 mod = pitch_mod & (1 << J); + + if (I == 0) + { + if (ch->needs_decode) + { + DecodeBlock(ch); + ch->needs_decode = FALSE; + ch->sample = ch->block[0]; + ch->sample_pointer = freq0 >> FIXED_POINT_SHIFT; + if (ch->sample_pointer == 0) + ch->sample_pointer = 1; + if (ch->sample_pointer > SOUND_DECODE_LENGTH) + ch->sample_pointer = SOUND_DECODE_LENGTH - 1; + + ch->next_sample=ch->block[ch->sample_pointer]; + ch->interpolate = 0; + + if (Settings.InterpolatedSound && freq0 < FIXED_POINT && !mod) + ch->interpolate = ((ch->next_sample - ch->sample) * + (long) freq0) / (long) FIXED_POINT; + } + VL [J] = (ch->sample * ch-> left_vol_level) / 128; + VR [J] = (ch->sample * ch->right_vol_level) / 128; + } + + unsigned long freq = freq0; + + if (mod) + freq = PITCH_MOD(freq, PreviousChannelSample); + + ch->env_error += ch->erate; + if (ch->env_error >= FIXED_POINT) + { + uint32 step = ch->env_error >> FIXED_POINT_SHIFT; + + switch (ch->state) + { + case SOUND_ATTACK: + ch->env_error &= FIXED_POINT_REMAINDER; + ch->envx += step << 1; + ch->envxx = ch->envx << ENVX_SHIFT; + + if (ch->envx >= 126) + { + ch->envx = 127; + ch->envxx = 127 << ENVX_SHIFT; + ch->state = SOUND_DECAY; + if (ch->sustain_level != 8) + { + S9xSetEnvRate (ch, ch->decay_rate, -1, + (MAX_ENVELOPE_HEIGHT * ch->sustain_level) + >> 3); + break; + } + ch->state = SOUND_SUSTAIN; + S9xSetEnvRate (ch, ch->sustain_rate, -1, 0); + } + break; + + case SOUND_DECAY: + while (ch->env_error >= FIXED_POINT) + { + ch->envxx = (ch->envxx >> 8) * 255; + ch->env_error -= FIXED_POINT; + } + ch->envx = ch->envxx >> ENVX_SHIFT; + if (ch->envx <= ch->envx_target) + { + if (ch->envx <= 0) + { + S9xAPUSetEndOfSample (J, ch); + goto stereo_exit; + } + ch->state = SOUND_SUSTAIN; + S9xSetEnvRate (ch, ch->sustain_rate, -1, 0); + } + break; + + case SOUND_SUSTAIN: + while (ch->env_error >= FIXED_POINT) + { + ch->envxx = (ch->envxx >> 8) * 255; + ch->env_error -= FIXED_POINT; + } + ch->envx = ch->envxx >> ENVX_SHIFT; + if (ch->envx <= 0) + { + S9xAPUSetEndOfSample (J, ch); + goto stereo_exit; + } + break; + case SOUND_RELEASE: + while (ch->env_error >= FIXED_POINT) + { + ch->envxx -= (MAX_ENVELOPE_HEIGHT << ENVX_SHIFT) / 256; + ch->env_error -= FIXED_POINT; + } + ch->envx = ch->envxx >> ENVX_SHIFT; + if (ch->envx <= 0) + { + S9xAPUSetEndOfSample (J, ch); + goto stereo_exit; + } + break; + + case SOUND_INCREASE_LINEAR: + ch->env_error &= FIXED_POINT_REMAINDER; + ch->envx += step << 1; + ch->envxx = ch->envx << ENVX_SHIFT; + + if (ch->envx >= 126) + { + ch->envx = 127; + ch->envxx = 127 << ENVX_SHIFT; + ch->state = SOUND_GAIN; + ch->mode = MODE_GAIN; + S9xSetEnvRate (ch, 0, -1, 0); + } + break; + + case SOUND_INCREASE_BENT_LINE: + if (ch->envx >= (MAX_ENVELOPE_HEIGHT * 3) / 4) + { + while (ch->env_error >= FIXED_POINT) + { + ch->envxx += (MAX_ENVELOPE_HEIGHT << ENVX_SHIFT) / 256; + ch->env_error -= FIXED_POINT; + } + ch->envx = ch->envxx >> ENVX_SHIFT; + } + else + { + ch->env_error &= FIXED_POINT_REMAINDER; + ch->envx += step << 1; + ch->envxx = ch->envx << ENVX_SHIFT; + } + + if (ch->envx >= 126) + { + ch->envx = 127; + ch->envxx = 127 << ENVX_SHIFT; + ch->state = SOUND_GAIN; + ch->mode = MODE_GAIN; + S9xSetEnvRate (ch, 0, -1, 0); + } + break; + + case SOUND_DECREASE_LINEAR: + ch->env_error &= FIXED_POINT_REMAINDER; + ch->envx -= step << 1; + ch->envxx = ch->envx << ENVX_SHIFT; + if (ch->envx <= 0) + { + S9xAPUSetEndOfSample (J, ch); + goto stereo_exit; + } + break; + + case SOUND_DECREASE_EXPONENTIAL: + while (ch->env_error >= FIXED_POINT) + { + ch->envxx = (ch->envxx >> 8) * 255; + ch->env_error -= FIXED_POINT; + } + ch->envx = ch->envxx >> ENVX_SHIFT; + if (ch->envx <= 0) + { + S9xAPUSetEndOfSample (J, ch); + goto stereo_exit; + } + break; + + case SOUND_GAIN: + S9xSetEnvRate (ch, 0, -1, 0); + break; + } + ch-> left_vol_level = (ch->envx * ch->volume_left) / 128; + ch->right_vol_level = (ch->envx * ch->volume_right) / 128; + VL [J] = (ch->sample * ch-> left_vol_level) / 128; + VR [J] = (ch->sample * ch->right_vol_level) / 128; + } + + ch->count += freq; + if (ch->count >= FIXED_POINT) + { + VL [J] = ch->count >> FIXED_POINT_SHIFT; + ch->sample_pointer += VL [J]; + ch->count &= FIXED_POINT_REMAINDER; + + ch->sample = ch->next_sample; + if (ch->sample_pointer >= SOUND_DECODE_LENGTH) + { + if (JUST_PLAYED_LAST_SAMPLE(ch)) + { + S9xAPUSetEndOfSample (J, ch); + goto stereo_exit; + } + do + { + ch->sample_pointer -= SOUND_DECODE_LENGTH; + if (ch->last_block) + { + if (!ch->loop) + { + ch->sample_pointer = LAST_SAMPLE; + ch->next_sample = ch->sample; + break; + } + else + { + S9xAPUSetEndX (J); + ch->last_block = FALSE; + uint8 *dir = S9xGetSampleAddress (ch->sample_number); + ch->block_pointer = READ_WORD(dir + 2); + } + } + DecodeBlock (ch); + } while (ch->sample_pointer >= SOUND_DECODE_LENGTH); + if (!JUST_PLAYED_LAST_SAMPLE (ch)) + ch->next_sample = ch->block [ch->sample_pointer]; + } + else + ch->next_sample = ch->block [ch->sample_pointer]; + + if (ch->type == SOUND_SAMPLE) + { + if (Settings.InterpolatedSound && freq < FIXED_POINT && !mod) + { + ch->interpolate = ((ch->next_sample - ch->sample) * + (long) freq) / (long) FIXED_POINT; + ch->sample = (int16) (ch->sample + (((ch->next_sample - ch->sample) * + (long) (ch->count)) / (long) FIXED_POINT)); + } + else + ch->interpolate = 0; + } + else + { + // Snes9x 1.53's SPC_DSP.cpp, by blargg + int feedback = (noise_gen << 13) ^ (noise_gen << 14); + noise_gen = (feedback & 0x4000) ^ (noise_gen >> 1); + ch->sample = (noise_gen << 17) >> 17; + ch->interpolate = 0; + } + + VL [J] = (ch->sample * ch-> left_vol_level) / 128; + VR [J] = (ch->sample * ch->right_vol_level) / 128; + } + else + { + if (ch->interpolate) + { + int32 s = (int32) ch->sample + ch->interpolate; + + CLIP16(s); + ch->sample = (int16) s; + VL [J] = (ch->sample * ch-> left_vol_level) / 128; + VR [J] = (ch->sample * ch->right_vol_level) / 128; + } + } + + OutputLeft += VL [J]; + OutputRight += VR [J]; + + if (pitch_mod & (1 << (J + 1))) + PreviousChannelSample = ch->sample * ch->envx; + +#ifndef FOREVER_FORWARD_STEREO + ch->echo_buf_ptr [I ^ Settings.ReverseStereo ] += VL [J]; + ch->echo_buf_ptr [I + (1 ^ Settings.ReverseStereo)] += VR [J]; +#else + ch->echo_buf_ptr [I ] += VL [J]; + ch->echo_buf_ptr [I + 1] += VR [J]; +#endif +stereo_exit: + ; + } +#ifndef FOREVER_FORWARD_STEREO + MixBuffer [I ^ Settings.ReverseStereo ] = OutputLeft; + MixBuffer [I + (1 ^ Settings.ReverseStereo)] = OutputRight; +#else + MixBuffer [I ] = OutputLeft; + MixBuffer [I + 1] = OutputRight; +#endif + } +} + +#ifdef __DJGPP +END_OF_FUNCTION(MixStereo_ReversedLoop); +#endif + +/* + * The layout of loops in this function is optimised for outputting a lot of + * samples, quickly ignoring channels that are muted so that they're not + * processed any further before the function returns. + * For each CHANNEL, samples are mixed into the mix buffer until the channel + * is muted or the sample count is met. + */ static inline void MixStereo (int sample_count) { - static int wave[SOUND_BUFFER_SIZE]; + static int32 wave[SOUND_BUFFER_SIZE]; int pitch_mod = SoundData.pitch_mod & ~APU.DSP[APU_NON]; @@ -1502,15 +1827,22 @@ void S9xMixSamples (uint8 *buffer, int sample_count) if (!so.mute_sound) { - memset (MixBuffer, 0, sample_count * sizeof (MixBuffer [0])); if (SoundData.echo_enable) memset (EchoBuffer, 0, sample_count * sizeof (EchoBuffer [0])); #ifndef FOREVER_STEREO if (so.stereo) + { #endif - MixStereo (sample_count); + if (sample_count < 12) + MixStereo_ReversedLoop (sample_count); + else + { + memset (MixBuffer, 0, sample_count * sizeof (MixBuffer [0])); + MixStereo (sample_count); + } #ifndef FOREVER_STEREO + } else MixMono (sample_count); #endif @@ -1537,7 +1869,7 @@ void S9xMixSamples (uint8 *buffer, int sample_count) { #endif // 16-bit stereo sound with echo enabled ... - if (SoundData.no_filter) + if (FilterTapDefinitionBitfield == 0) { // ... but no filter defined. for (J = 0; J < sample_count; J++) @@ -1567,13 +1899,13 @@ void S9xMixSamples (uint8 *buffer, int sample_count) Loop [(Z - 0) & 15] = E; E = E * FilterTaps [0]; - E += Loop [(Z - 2) & 15] * FilterTaps [1]; - E += Loop [(Z - 4) & 15] * FilterTaps [2]; - E += Loop [(Z - 6) & 15] * FilterTaps [3]; - E += Loop [(Z - 8) & 15] * FilterTaps [4]; - E += Loop [(Z - 10) & 15] * FilterTaps [5]; - E += Loop [(Z - 12) & 15] * FilterTaps [6]; - E += Loop [(Z - 14) & 15] * FilterTaps [7]; + if (FilterTapDefinitionBitfield & 0x02) E += Loop [(Z - 2) & 15] * FilterTaps [1]; + if (FilterTapDefinitionBitfield & 0x04) E += Loop [(Z - 4) & 15] * FilterTaps [2]; + if (FilterTapDefinitionBitfield & 0x08) E += Loop [(Z - 6) & 15] * FilterTaps [3]; + if (FilterTapDefinitionBitfield & 0x10) E += Loop [(Z - 8) & 15] * FilterTaps [4]; + if (FilterTapDefinitionBitfield & 0x20) E += Loop [(Z - 10) & 15] * FilterTaps [5]; + if (FilterTapDefinitionBitfield & 0x40) E += Loop [(Z - 12) & 15] * FilterTaps [6]; + if (FilterTapDefinitionBitfield & 0x80) E += Loop [(Z - 14) & 15] * FilterTaps [7]; E /= 128; Z++; @@ -1596,7 +1928,7 @@ void S9xMixSamples (uint8 *buffer, int sample_count) else { // 16-bit mono sound with echo enabled... - if (SoundData.no_filter) + if (FilterTapDefinitionBitfield == 0) { // ... no filter defined for (J = 0; J < sample_count; J++) @@ -1625,13 +1957,13 @@ void S9xMixSamples (uint8 *buffer, int sample_count) Loop [(Z - 0) & 7] = E; E = E * FilterTaps [0]; - E += Loop [(Z - 1) & 7] * FilterTaps [1]; - E += Loop [(Z - 2) & 7] * FilterTaps [2]; - E += Loop [(Z - 3) & 7] * FilterTaps [3]; - E += Loop [(Z - 4) & 7] * FilterTaps [4]; - E += Loop [(Z - 5) & 7] * FilterTaps [5]; - E += Loop [(Z - 6) & 7] * FilterTaps [6]; - E += Loop [(Z - 7) & 7] * FilterTaps [7]; + if (FilterTapDefinitionBitfield & 0x02) E += Loop [(Z - 1) & 7] * FilterTaps [1]; + if (FilterTapDefinitionBitfield & 0x04) E += Loop [(Z - 2) & 7] * FilterTaps [2]; + if (FilterTapDefinitionBitfield & 0x08) E += Loop [(Z - 3) & 7] * FilterTaps [3]; + if (FilterTapDefinitionBitfield & 0x10) E += Loop [(Z - 4) & 7] * FilterTaps [4]; + if (FilterTapDefinitionBitfield & 0x20) E += Loop [(Z - 5) & 7] * FilterTaps [5]; + if (FilterTapDefinitionBitfield & 0x40) E += Loop [(Z - 6) & 7] * FilterTaps [6]; + if (FilterTapDefinitionBitfield & 0x80) E += Loop [(Z - 7) & 7] * FilterTaps [7]; E /= 128; Z++; @@ -1691,7 +2023,7 @@ void S9xMixSamples (uint8 *buffer, int sample_count) if (so.stereo) { // 8-bit stereo sound with echo enabled... - if (SoundData.no_filter) + if (FilterTapDefinitionBitfield == 0) { // ... but no filter for (J = 0; J < sample_count; J++) @@ -1720,13 +2052,13 @@ void S9xMixSamples (uint8 *buffer, int sample_count) Loop [(Z - 0) & 15] = E; E = E * FilterTaps [0]; - E += Loop [(Z - 2) & 15] * FilterTaps [1]; - E += Loop [(Z - 4) & 15] * FilterTaps [2]; - E += Loop [(Z - 6) & 15] * FilterTaps [3]; - E += Loop [(Z - 8) & 15] * FilterTaps [4]; - E += Loop [(Z - 10) & 15] * FilterTaps [5]; - E += Loop [(Z - 12) & 15] * FilterTaps [6]; - E += Loop [(Z - 14) & 15] * FilterTaps [7]; + if (FilterTapDefinitionBitfield & 0x02) E += Loop [(Z - 2) & 15] * FilterTaps [1]; + if (FilterTapDefinitionBitfield & 0x04) E += Loop [(Z - 4) & 15] * FilterTaps [2]; + if (FilterTapDefinitionBitfield & 0x08) E += Loop [(Z - 6) & 15] * FilterTaps [3]; + if (FilterTapDefinitionBitfield & 0x10) E += Loop [(Z - 8) & 15] * FilterTaps [4]; + if (FilterTapDefinitionBitfield & 0x20) E += Loop [(Z - 10) & 15] * FilterTaps [5]; + if (FilterTapDefinitionBitfield & 0x40) E += Loop [(Z - 12) & 15] * FilterTaps [6]; + if (FilterTapDefinitionBitfield & 0x80) E += Loop [(Z - 14) & 15] * FilterTaps [7]; E /= 128; Z++; @@ -1747,7 +2079,7 @@ void S9xMixSamples (uint8 *buffer, int sample_count) else { // 8-bit mono sound with echo enabled... - if (SoundData.no_filter) + if (FilterTapDefinitionBitfield == 0) { // ... but no filter. for (J = 0; J < sample_count; J++) @@ -1775,13 +2107,13 @@ void S9xMixSamples (uint8 *buffer, int sample_count) Loop [(Z - 0) & 7] = E; E = E * FilterTaps [0]; - E += Loop [(Z - 1) & 7] * FilterTaps [1]; - E += Loop [(Z - 2) & 7] * FilterTaps [2]; - E += Loop [(Z - 3) & 7] * FilterTaps [3]; - E += Loop [(Z - 4) & 7] * FilterTaps [4]; - E += Loop [(Z - 5) & 7] * FilterTaps [5]; - E += Loop [(Z - 6) & 7] * FilterTaps [6]; - E += Loop [(Z - 7) & 7] * FilterTaps [7]; + if (FilterTapDefinitionBitfield & 0x02) E += Loop [(Z - 1) & 7] * FilterTaps [1]; + if (FilterTapDefinitionBitfield & 0x04) E += Loop [(Z - 2) & 7] * FilterTaps [2]; + if (FilterTapDefinitionBitfield & 0x08) E += Loop [(Z - 3) & 7] * FilterTaps [3]; + if (FilterTapDefinitionBitfield & 0x10) E += Loop [(Z - 4) & 7] * FilterTaps [4]; + if (FilterTapDefinitionBitfield & 0x20) E += Loop [(Z - 5) & 7] * FilterTaps [5]; + if (FilterTapDefinitionBitfield & 0x40) E += Loop [(Z - 6) & 7] * FilterTaps [6]; + if (FilterTapDefinitionBitfield & 0x80) E += Loop [(Z - 7) & 7] * FilterTaps [7]; E /= 128; Z++; @@ -1894,7 +2226,7 @@ void S9xResetSound (bool8 full) so.err_rate = (uint32) (FIXED_POINT * SNES_SCANLINE_TIME / (1.0 / so.playback_rate)); else so.err_rate = 0; - SoundData.no_filter = TRUE; + FilterTapDefinitionBitfield = 0; } void S9xSetPlaybackRate (uint32 playback_rate) diff --git a/source/soundux.h b/source/soundux.h index 8fb8396..b603df8 100644 --- a/source/soundux.h +++ b/source/soundux.h @@ -212,7 +212,7 @@ typedef struct // Just incase they are needed in the future, for snapshot compatibility. uint32 dummy [3]; Channel channels [NUM_CHANNELS]; - bool8 no_filter; + // bool8 no_filter; int master_volume [2]; /* range is -128 .. 127 */ int echo_volume [2]; /* range is -128 .. 127 */ int noise_hertz; |