From 61f80edda36390c8b8795c2752f1e82ac828dca0 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sat, 3 Sep 2016 11:51:00 +0200 Subject: [PATCH 1/8] SasAudio: Straightforward implementation of linear interpolation. Not sure about the performance impact, but calling ReadSamples with 1 as an argument can't be terribly fast. So this might need some redesign before merge, but should be a good starting point. --- Core/HW/SasAudio.cpp | 70 +++++++++++++------------------------------- Core/HW/SasAudio.h | 2 -- 2 files changed, 20 insertions(+), 52 deletions(-) diff --git a/Core/HW/SasAudio.cpp b/Core/HW/SasAudio.cpp index ceae931a55f6..688b45853c84 100644 --- a/Core/HW/SasAudio.cpp +++ b/Core/HW/SasAudio.cpp @@ -337,7 +337,6 @@ SasInstance::SasInstance() sendBuffer(0), sendBufferDownsampled(0), sendBufferProcessed(0), - resampleBuffer(0), grainSize(0) { #ifdef AUDIO_TO_FILE audioDump = fopen("D:\\audio.raw", "wb"); @@ -376,10 +375,8 @@ void SasInstance::ClearGrainSize() { delete[] sendBuffer; delete[] sendBufferDownsampled; delete[] sendBufferProcessed; - delete[] resampleBuffer; mixBuffer = nullptr; sendBuffer = nullptr; - resampleBuffer = nullptr; sendBufferDownsampled = nullptr; sendBufferProcessed = nullptr; } @@ -392,7 +389,6 @@ void SasInstance::SetGrainSize(int newGrainSize) { delete[] sendBuffer; delete[] sendBufferDownsampled; delete[] sendBufferProcessed; - delete[] resampleBuffer; mixBuffer = new s32[grainSize * 2]; sendBuffer = new s32[grainSize * 2]; @@ -402,10 +398,6 @@ void SasInstance::SetGrainSize(int newGrainSize) { memset(sendBuffer, 0, sizeof(int) * grainSize * 2); memset(sendBufferDownsampled, 0, sizeof(s16) * grainSize); memset(sendBufferProcessed, 0, sizeof(s16) * grainSize * 2); - - // 2 samples padding at the start, that's where we copy the two last samples from the channel - // so that we can do bicubic resampling if necessary. Plus 1 for smoothness hackery. - resampleBuffer = new s16[grainSize * 4 + 3]; } int SasInstance::EstimateMixUs() { @@ -459,9 +451,7 @@ void SasVoice::ReadSamples(s16 *output, int numSamples) { atrac3.getNextSamples(output, numSamples); break; default: - { - memset(output, 0, numSamples * sizeof(s16)); - } + memset(output, 0, numSamples * sizeof(s16)); break; } } @@ -493,47 +483,28 @@ void SasInstance::MixVoice(SasVoice &voice) { break; // else fallthrough! Don't change the check above. default: - // Load resample history (so we can use a wide filter) - resampleBuffer[0] = voice.resampleHist[0]; - resampleBuffer[1] = voice.resampleHist[1]; - - // Figure out number of samples to read. - // Actually this is not entirely correct - we need to get one extra sample, and store it - // for the next time around. A little complicated... - // But for now, see Smoothness HACKERY below :P - u32 numSamples = ((u32)voice.sampleFrac + (u32)grainSize * (u32)voice.pitch) >> PSP_SAS_PITCH_BASE_SHIFT; - if ((int)numSamples > grainSize * 4) { - ERROR_LOG(SASMIX, "numSamples too large, clamping: %i vs %i", numSamples, grainSize * 4); - numSamples = grainSize * 4; - } - // This feels a bit hacky. The first 32 samples after a keyon are 0s. - const bool ignorePitch = voice.type == VOICETYPE_PCM && voice.pitch > PSP_SAS_PITCH_BASE; + int delay = 0; if (voice.envelope.NeedsKeyOn()) { - int delay = ignorePitch ? 32 : (32 * (u32)voice.pitch) >> PSP_SAS_PITCH_BASE_SHIFT; + const bool ignorePitch = voice.type == VOICETYPE_PCM && voice.pitch > PSP_SAS_PITCH_BASE; + delay = ignorePitch ? 32 : (32 * (u32)voice.pitch) >> PSP_SAS_PITCH_BASE_SHIFT; // VAG seems to have an extra sample delay (not shared by PCM.) if (voice.type == VOICETYPE_VAG) ++delay; - voice.ReadSamples(resampleBuffer + 2 + delay, numSamples - delay); - } else { - voice.ReadSamples(resampleBuffer + 2, numSamples); } - // Smoothness HACKERY - resampleBuffer[2 + numSamples] = resampleBuffer[2 + numSamples - 1]; - - // Save resample history - voice.resampleHist[0] = resampleBuffer[2 + numSamples - 2]; - voice.resampleHist[1] = resampleBuffer[2 + numSamples - 1]; - // Resample to the correct pitch, writing exactly "grainSize" samples. - // This is a HORRIBLE resampler by the way. // TODO: Special case no-resample case (and 2x and 0.5x) for speed, it's not uncommon - u32 sampleFrac = voice.sampleFrac; - for (int i = 0; i < grainSize; i++) { - // For now: nearest neighbour, not even using the resample history at all. - int sample = resampleBuffer[sampleFrac / PSP_SAS_PITCH_BASE + 2]; + for (int i = delay; i < grainSize; i++) { + while (sampleFrac >= PSP_SAS_PITCH_BASE) { + voice.resampleHist[0] = voice.resampleHist[1]; + voice.ReadSamples(&voice.resampleHist[1], 1); + sampleFrac -= PSP_SAS_PITCH_BASE; + } + + // Linear interpolation. Good enough. Need to make resampleHist bigger if we want more. + int sample = (voice.resampleHist[0] * (PSP_SAS_PITCH_BASE - 1 - (int)sampleFrac) + voice.resampleHist[1] * (int)sampleFrac) >> PSP_SAS_PITCH_BASE_SHIFT; sampleFrac += voice.pitch; // The maximum envelope height (PSP_SAS_ENVELOPE_HEIGHT_MAX) is (1 << 30) - 1. @@ -549,21 +520,17 @@ void SasInstance::MixVoice(SasVoice &voice) { // We mix into this 32-bit temp buffer and clip in a second loop // Ideally, the shift right should be there too but for now I'm concerned about // not overflowing. - mixBuffer[i * 2] += (sample * voice.volumeLeft ) >> 12; + mixBuffer[i * 2] += (sample * voice.volumeLeft) >> 12; mixBuffer[i * 2 + 1] += (sample * voice.volumeRight) >> 12; sendBuffer[i * 2] += sample * voice.effectLeft >> 12; sendBuffer[i * 2 + 1] += sample * voice.effectRight >> 12; } voice.sampleFrac = sampleFrac; - // Let's hope grainSize is a power of 2. - //voice.sampleFrac &= grainSize * PSP_SAS_PITCH_BASE - 1; - voice.sampleFrac -= numSamples * PSP_SAS_PITCH_BASE; if (voice.HaveSamplesEnded()) voice.envelope.End(); - if (voice.envelope.HasEnded()) - { + if (voice.envelope.HasEnded()) { // NOTICE_LOG(SCESAS, "Hit end of envelope"); voice.playing = false; voice.on = false; @@ -711,8 +678,11 @@ void SasInstance::DoState(PointerWrap &p) { if (sendBuffer != NULL && grainSize > 0) { p.DoArray(sendBuffer, grainSize * 2); } - if (resampleBuffer != NULL && grainSize > 0) { - p.DoArray(resampleBuffer, grainSize * 4 + 3); + if (sendBuffer != NULL && grainSize > 0) { + // Backwards compat + int16_t *resampleBuf = new int16_t[grainSize * 4 + 3](); + p.DoArray(resampleBuf, grainSize * 4 + 3); + delete[] resampleBuf; } int n = PSP_SAS_VOICES_MAX; diff --git a/Core/HW/SasAudio.h b/Core/HW/SasAudio.h index fd4283e2f6b5..80d7c22e917b 100644 --- a/Core/HW/SasAudio.h +++ b/Core/HW/SasAudio.h @@ -289,8 +289,6 @@ class SasInstance { s16 *sendBufferDownsampled; s16 *sendBufferProcessed; - s16 *resampleBuffer; - FILE *audioDump; void Mix(u32 outAddr, u32 inAddr = 0, int leftVol = 0, int rightVol = 0); From 47df134b66816ffd5d4cb9218ec1812645db8382 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Tue, 20 Dec 2016 20:37:49 +0100 Subject: [PATCH 2/8] Optimization step 1: Separate the loops, save history from buffer. --- Core/HW/SasAudio.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Core/HW/SasAudio.cpp b/Core/HW/SasAudio.cpp index 688b45853c84..a4556c5224cb 100644 --- a/Core/HW/SasAudio.cpp +++ b/Core/HW/SasAudio.cpp @@ -495,11 +495,27 @@ void SasInstance::MixVoice(SasVoice &voice) { // Resample to the correct pitch, writing exactly "grainSize" samples. // TODO: Special case no-resample case (and 2x and 0.5x) for speed, it's not uncommon + int16_t temp[256 * 4]; + int tempPos = 0; + + // Two passes: First read, then resample. u32 sampleFrac = voice.sampleFrac; + temp[tempPos++] = voice.resampleHist[0]; + temp[tempPos++] = voice.resampleHist[1]; + for (int i = delay; i < grainSize; i++) { + while (sampleFrac >= PSP_SAS_PITCH_BASE) { + voice.ReadSamples(&temp[tempPos++], 1); + sampleFrac -= PSP_SAS_PITCH_BASE; + } + sampleFrac += voice.pitch; + } + + sampleFrac = voice.sampleFrac; + tempPos = 2; // skip the two history samples in the read buffer for now for (int i = delay; i < grainSize; i++) { while (sampleFrac >= PSP_SAS_PITCH_BASE) { voice.resampleHist[0] = voice.resampleHist[1]; - voice.ReadSamples(&voice.resampleHist[1], 1); + voice.resampleHist[1] = temp[tempPos++]; sampleFrac -= PSP_SAS_PITCH_BASE; } @@ -526,6 +542,9 @@ void SasInstance::MixVoice(SasVoice &voice) { sendBuffer[i * 2 + 1] += sample * voice.effectRight >> 12; } + voice.resampleHist[0] = temp[tempPos - 2]; + voice.resampleHist[1] = temp[tempPos - 1]; + voice.sampleFrac = sampleFrac; if (voice.HaveSamplesEnded()) From 064795216e622510895187961af34f949d65bf93 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Tue, 20 Dec 2016 21:05:23 +0100 Subject: [PATCH 3/8] Add parallel resampling algorithm that reads directly from the temp buf --- Core/HW/SasAudio.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Core/HW/SasAudio.cpp b/Core/HW/SasAudio.cpp index a4556c5224cb..17a8b574f828 100644 --- a/Core/HW/SasAudio.cpp +++ b/Core/HW/SasAudio.cpp @@ -495,8 +495,8 @@ void SasInstance::MixVoice(SasVoice &voice) { // Resample to the correct pitch, writing exactly "grainSize" samples. // TODO: Special case no-resample case (and 2x and 0.5x) for speed, it's not uncommon - int16_t temp[256 * 4]; - int tempPos = 0; + int16_t temp[256 * 4]{}; + int tempPos = delay; // Two passes: First read, then resample. u32 sampleFrac = voice.sampleFrac; @@ -512,6 +512,7 @@ void SasInstance::MixVoice(SasVoice &voice) { sampleFrac = voice.sampleFrac; tempPos = 2; // skip the two history samples in the read buffer for now + int altSampleFrac = voice.sampleFrac; for (int i = delay; i < grainSize; i++) { while (sampleFrac >= PSP_SAS_PITCH_BASE) { voice.resampleHist[0] = voice.resampleHist[1]; @@ -519,9 +520,16 @@ void SasInstance::MixVoice(SasVoice &voice) { sampleFrac -= PSP_SAS_PITCH_BASE; } + const int16_t *s = voice.resampleHist; + const int16_t *altS = temp + (altSampleFrac >> PSP_SAS_PITCH_BASE_SHIFT); + + if (altS[0] != s[0] || altS[1] != s[1]) + Crash(); + // Linear interpolation. Good enough. Need to make resampleHist bigger if we want more. - int sample = (voice.resampleHist[0] * (PSP_SAS_PITCH_BASE - 1 - (int)sampleFrac) + voice.resampleHist[1] * (int)sampleFrac) >> PSP_SAS_PITCH_BASE_SHIFT; + int sample = (s[0] * (PSP_SAS_PITCH_BASE - 1 - (int)sampleFrac) + s[1] * (int)sampleFrac) >> PSP_SAS_PITCH_BASE_SHIFT; sampleFrac += voice.pitch; + altSampleFrac += voice.pitch; // The maximum envelope height (PSP_SAS_ENVELOPE_HEIGHT_MAX) is (1 << 30) - 1. // Reduce it to 14 bits, by shifting off 15. Round up by adding (1 << 14) first. @@ -546,6 +554,9 @@ void SasInstance::MixVoice(SasVoice &voice) { voice.resampleHist[1] = temp[tempPos - 1]; voice.sampleFrac = sampleFrac; + altSampleFrac -= (tempPos - 2) * PSP_SAS_PITCH_BASE; + if (voice.sampleFrac != altSampleFrac) + Crash(); if (voice.HaveSamplesEnded()) voice.envelope.End(); From 451388070a8768c70e80b6c030f0431f68f2ed77 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Tue, 20 Dec 2016 21:37:31 +0100 Subject: [PATCH 4/8] Fix bug. Now use the quick inner loop, just need to redo the sample reader... --- Core/HW/SasAudio.cpp | 12 ++++++------ Core/HW/SasAudio.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Core/HW/SasAudio.cpp b/Core/HW/SasAudio.cpp index 17a8b574f828..f9ea8c319c7c 100644 --- a/Core/HW/SasAudio.cpp +++ b/Core/HW/SasAudio.cpp @@ -496,7 +496,7 @@ void SasInstance::MixVoice(SasVoice &voice) { // Resample to the correct pitch, writing exactly "grainSize" samples. // TODO: Special case no-resample case (and 2x and 0.5x) for speed, it's not uncommon int16_t temp[256 * 4]{}; - int tempPos = delay; + int tempPos = 0; // Two passes: First read, then resample. u32 sampleFrac = voice.sampleFrac; @@ -520,10 +520,10 @@ void SasInstance::MixVoice(SasVoice &voice) { sampleFrac -= PSP_SAS_PITCH_BASE; } - const int16_t *s = voice.resampleHist; - const int16_t *altS = temp + (altSampleFrac >> PSP_SAS_PITCH_BASE_SHIFT); + const int16_t *oldS = voice.resampleHist; + const int16_t *s = temp + (altSampleFrac >> PSP_SAS_PITCH_BASE_SHIFT); - if (altS[0] != s[0] || altS[1] != s[1]) + if (s[0] != oldS[0] || s[1] != oldS[1]) Crash(); // Linear interpolation. Good enough. Need to make resampleHist bigger if we want more. @@ -553,10 +553,10 @@ void SasInstance::MixVoice(SasVoice &voice) { voice.resampleHist[0] = temp[tempPos - 2]; voice.resampleHist[1] = temp[tempPos - 1]; - voice.sampleFrac = sampleFrac; altSampleFrac -= (tempPos - 2) * PSP_SAS_PITCH_BASE; - if (voice.sampleFrac != altSampleFrac) + if (sampleFrac != altSampleFrac) Crash(); + voice.sampleFrac = altSampleFrac; if (voice.HaveSamplesEnded()) voice.envelope.End(); diff --git a/Core/HW/SasAudio.h b/Core/HW/SasAudio.h index 80d7c22e917b..e26427ca6ef4 100644 --- a/Core/HW/SasAudio.h +++ b/Core/HW/SasAudio.h @@ -250,7 +250,7 @@ struct SasVoice { int pcmLoopPos; int sampleRate; - int sampleFrac; + uint32_t sampleFrac; int pitch; bool loop; From e802fb0b73b85df46322f5f7b7d005d7c76e2887 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Tue, 20 Dec 2016 21:42:11 +0100 Subject: [PATCH 5/8] Remove the verification. --- Core/HW/SasAudio.cpp | 23 ++++------------------- Core/HW/SasAudio.h | 1 + 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/Core/HW/SasAudio.cpp b/Core/HW/SasAudio.cpp index f9ea8c319c7c..ae0dc707ab8b 100644 --- a/Core/HW/SasAudio.cpp +++ b/Core/HW/SasAudio.cpp @@ -511,25 +511,13 @@ void SasInstance::MixVoice(SasVoice &voice) { } sampleFrac = voice.sampleFrac; - tempPos = 2; // skip the two history samples in the read buffer for now - int altSampleFrac = voice.sampleFrac; for (int i = delay; i < grainSize; i++) { - while (sampleFrac >= PSP_SAS_PITCH_BASE) { - voice.resampleHist[0] = voice.resampleHist[1]; - voice.resampleHist[1] = temp[tempPos++]; - sampleFrac -= PSP_SAS_PITCH_BASE; - } - - const int16_t *oldS = voice.resampleHist; - const int16_t *s = temp + (altSampleFrac >> PSP_SAS_PITCH_BASE_SHIFT); - - if (s[0] != oldS[0] || s[1] != oldS[1]) - Crash(); + const int16_t *s = temp + (sampleFrac >> PSP_SAS_PITCH_BASE_SHIFT); // Linear interpolation. Good enough. Need to make resampleHist bigger if we want more. - int sample = (s[0] * (PSP_SAS_PITCH_BASE - 1 - (int)sampleFrac) + s[1] * (int)sampleFrac) >> PSP_SAS_PITCH_BASE_SHIFT; + int f = sampleFrac & PSP_SAS_PITCH_MASK; + int sample = (s[0] * (PSP_SAS_PITCH_MASK - f) + s[1] * f) >> PSP_SAS_PITCH_BASE_SHIFT; sampleFrac += voice.pitch; - altSampleFrac += voice.pitch; // The maximum envelope height (PSP_SAS_ENVELOPE_HEIGHT_MAX) is (1 << 30) - 1. // Reduce it to 14 bits, by shifting off 15. Round up by adding (1 << 14) first. @@ -553,10 +541,7 @@ void SasInstance::MixVoice(SasVoice &voice) { voice.resampleHist[0] = temp[tempPos - 2]; voice.resampleHist[1] = temp[tempPos - 1]; - altSampleFrac -= (tempPos - 2) * PSP_SAS_PITCH_BASE; - if (sampleFrac != altSampleFrac) - Crash(); - voice.sampleFrac = altSampleFrac; + voice.sampleFrac = sampleFrac - (tempPos - 2) * PSP_SAS_PITCH_BASE;; if (voice.HaveSamplesEnded()) voice.envelope.End(); diff --git a/Core/HW/SasAudio.h b/Core/HW/SasAudio.h index e26427ca6ef4..d20bd82f9900 100644 --- a/Core/HW/SasAudio.h +++ b/Core/HW/SasAudio.h @@ -33,6 +33,7 @@ enum { PSP_SAS_PITCH_MIN = 0x0000, PSP_SAS_PITCH_BASE = 0x1000, + PSP_SAS_PITCH_MASK = 0xFFF, PSP_SAS_PITCH_BASE_SHIFT = 12, PSP_SAS_PITCH_MAX = 0x4000, From 6782f7c75dd16b2b11bb95372ff81f1ccdd4599f Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Tue, 20 Dec 2016 21:47:50 +0100 Subject: [PATCH 6/8] Just figure out how many samples to read, then read it, instead of one-by-one. --- Core/HW/SasAudio.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Core/HW/SasAudio.cpp b/Core/HW/SasAudio.cpp index ae0dc707ab8b..f23b9e0ffc27 100644 --- a/Core/HW/SasAudio.cpp +++ b/Core/HW/SasAudio.cpp @@ -502,13 +502,20 @@ void SasInstance::MixVoice(SasVoice &voice) { u32 sampleFrac = voice.sampleFrac; temp[tempPos++] = voice.resampleHist[0]; temp[tempPos++] = voice.resampleHist[1]; + + int samplesToRead = 0; for (int i = delay; i < grainSize; i++) { while (sampleFrac >= PSP_SAS_PITCH_BASE) { - voice.ReadSamples(&temp[tempPos++], 1); + samplesToRead++; sampleFrac -= PSP_SAS_PITCH_BASE; } sampleFrac += voice.pitch; } + voice.ReadSamples(&temp[tempPos], samplesToRead); + tempPos += samplesToRead; + + if (tempPos - 2 != samplesToRead) + Crash(); sampleFrac = voice.sampleFrac; for (int i = delay; i < grainSize; i++) { From ff4b99dd97986e3edbc09151c083abd92306e633 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Tue, 20 Dec 2016 21:52:25 +0100 Subject: [PATCH 7/8] Replace for-loop with multiplication --- Core/HW/SasAudio.cpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/Core/HW/SasAudio.cpp b/Core/HW/SasAudio.cpp index f23b9e0ffc27..6977b02c521a 100644 --- a/Core/HW/SasAudio.cpp +++ b/Core/HW/SasAudio.cpp @@ -495,7 +495,7 @@ void SasInstance::MixVoice(SasVoice &voice) { // Resample to the correct pitch, writing exactly "grainSize" samples. // TODO: Special case no-resample case (and 2x and 0.5x) for speed, it's not uncommon - int16_t temp[256 * 4]{}; + int16_t temp[256 * 4]; int tempPos = 0; // Two passes: First read, then resample. @@ -504,20 +504,15 @@ void SasInstance::MixVoice(SasVoice &voice) { temp[tempPos++] = voice.resampleHist[1]; int samplesToRead = 0; - for (int i = delay; i < grainSize; i++) { - while (sampleFrac >= PSP_SAS_PITCH_BASE) { - samplesToRead++; - sampleFrac -= PSP_SAS_PITCH_BASE; - } - sampleFrac += voice.pitch; + uint32_t t = sampleFrac + voice.pitch * (grainSize - delay); + while (t >= PSP_SAS_PITCH_BASE) { + samplesToRead++; + t -= PSP_SAS_PITCH_BASE; } + voice.ReadSamples(&temp[tempPos], samplesToRead); tempPos += samplesToRead; - if (tempPos - 2 != samplesToRead) - Crash(); - - sampleFrac = voice.sampleFrac; for (int i = delay; i < grainSize; i++) { const int16_t *s = temp + (sampleFrac >> PSP_SAS_PITCH_BASE_SHIFT); From 1e095951dd887a538a1c151df3fdec83c907c48f Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Tue, 20 Dec 2016 22:02:40 +0100 Subject: [PATCH 8/8] Replace while loop with division (technically a right shift). --- Core/HW/SasAudio.cpp | 19 ++++++------------- Core/HW/SasAudio.h | 1 + 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/Core/HW/SasAudio.cpp b/Core/HW/SasAudio.cpp index 6977b02c521a..46a23a85c48a 100644 --- a/Core/HW/SasAudio.cpp +++ b/Core/HW/SasAudio.cpp @@ -495,23 +495,16 @@ void SasInstance::MixVoice(SasVoice &voice) { // Resample to the correct pitch, writing exactly "grainSize" samples. // TODO: Special case no-resample case (and 2x and 0.5x) for speed, it's not uncommon - int16_t temp[256 * 4]; - int tempPos = 0; + int16_t temp[PSP_SAS_MAX_GRAIN + 2]; // Two passes: First read, then resample. u32 sampleFrac = voice.sampleFrac; - temp[tempPos++] = voice.resampleHist[0]; - temp[tempPos++] = voice.resampleHist[1]; - - int samplesToRead = 0; - uint32_t t = sampleFrac + voice.pitch * (grainSize - delay); - while (t >= PSP_SAS_PITCH_BASE) { - samplesToRead++; - t -= PSP_SAS_PITCH_BASE; - } + temp[0] = voice.resampleHist[0]; + temp[1] = voice.resampleHist[1]; - voice.ReadSamples(&temp[tempPos], samplesToRead); - tempPos += samplesToRead; + int samplesToRead = (sampleFrac + voice.pitch * (grainSize - delay)) >> PSP_SAS_PITCH_BASE_SHIFT; + voice.ReadSamples(&temp[2], samplesToRead); + int tempPos = 2 + samplesToRead; for (int i = delay; i < grainSize; i++) { const int16_t *s = temp + (sampleFrac >> PSP_SAS_PITCH_BASE_SHIFT); diff --git a/Core/HW/SasAudio.h b/Core/HW/SasAudio.h index d20bd82f9900..195056d47673 100644 --- a/Core/HW/SasAudio.h +++ b/Core/HW/SasAudio.h @@ -38,6 +38,7 @@ enum { PSP_SAS_PITCH_MAX = 0x4000, PSP_SAS_VOL_MAX = 0x1000, + PSP_SAS_MAX_GRAIN = 1024, // VERY conservative! 256 is quite common but don't think I've ever seen bigger. PSP_SAS_ADSR_CURVE_MODE_LINEAR_INCREASE = 0, PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE = 1,