mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
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.
This commit is contained in:
parent
e9bea75a07
commit
61f80edda3
2 changed files with 20 additions and 52 deletions
|
@ -337,7 +337,6 @@ SasInstance::SasInstance()
|
||||||
sendBuffer(0),
|
sendBuffer(0),
|
||||||
sendBufferDownsampled(0),
|
sendBufferDownsampled(0),
|
||||||
sendBufferProcessed(0),
|
sendBufferProcessed(0),
|
||||||
resampleBuffer(0),
|
|
||||||
grainSize(0) {
|
grainSize(0) {
|
||||||
#ifdef AUDIO_TO_FILE
|
#ifdef AUDIO_TO_FILE
|
||||||
audioDump = fopen("D:\\audio.raw", "wb");
|
audioDump = fopen("D:\\audio.raw", "wb");
|
||||||
|
@ -376,10 +375,8 @@ void SasInstance::ClearGrainSize() {
|
||||||
delete[] sendBuffer;
|
delete[] sendBuffer;
|
||||||
delete[] sendBufferDownsampled;
|
delete[] sendBufferDownsampled;
|
||||||
delete[] sendBufferProcessed;
|
delete[] sendBufferProcessed;
|
||||||
delete[] resampleBuffer;
|
|
||||||
mixBuffer = nullptr;
|
mixBuffer = nullptr;
|
||||||
sendBuffer = nullptr;
|
sendBuffer = nullptr;
|
||||||
resampleBuffer = nullptr;
|
|
||||||
sendBufferDownsampled = nullptr;
|
sendBufferDownsampled = nullptr;
|
||||||
sendBufferProcessed = nullptr;
|
sendBufferProcessed = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -392,7 +389,6 @@ void SasInstance::SetGrainSize(int newGrainSize) {
|
||||||
delete[] sendBuffer;
|
delete[] sendBuffer;
|
||||||
delete[] sendBufferDownsampled;
|
delete[] sendBufferDownsampled;
|
||||||
delete[] sendBufferProcessed;
|
delete[] sendBufferProcessed;
|
||||||
delete[] resampleBuffer;
|
|
||||||
|
|
||||||
mixBuffer = new s32[grainSize * 2];
|
mixBuffer = new s32[grainSize * 2];
|
||||||
sendBuffer = 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(sendBuffer, 0, sizeof(int) * grainSize * 2);
|
||||||
memset(sendBufferDownsampled, 0, sizeof(s16) * grainSize);
|
memset(sendBufferDownsampled, 0, sizeof(s16) * grainSize);
|
||||||
memset(sendBufferProcessed, 0, sizeof(s16) * grainSize * 2);
|
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() {
|
int SasInstance::EstimateMixUs() {
|
||||||
|
@ -459,9 +451,7 @@ void SasVoice::ReadSamples(s16 *output, int numSamples) {
|
||||||
atrac3.getNextSamples(output, numSamples);
|
atrac3.getNextSamples(output, numSamples);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
{
|
memset(output, 0, numSamples * sizeof(s16));
|
||||||
memset(output, 0, numSamples * sizeof(s16));
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -493,47 +483,28 @@ void SasInstance::MixVoice(SasVoice &voice) {
|
||||||
break;
|
break;
|
||||||
// else fallthrough! Don't change the check above.
|
// else fallthrough! Don't change the check above.
|
||||||
default:
|
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.
|
// 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()) {
|
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.)
|
// VAG seems to have an extra sample delay (not shared by PCM.)
|
||||||
if (voice.type == VOICETYPE_VAG)
|
if (voice.type == VOICETYPE_VAG)
|
||||||
++delay;
|
++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.
|
// 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
|
// TODO: Special case no-resample case (and 2x and 0.5x) for speed, it's not uncommon
|
||||||
|
|
||||||
u32 sampleFrac = voice.sampleFrac;
|
u32 sampleFrac = voice.sampleFrac;
|
||||||
for (int i = 0; i < grainSize; i++) {
|
for (int i = delay; i < grainSize; i++) {
|
||||||
// For now: nearest neighbour, not even using the resample history at all.
|
while (sampleFrac >= PSP_SAS_PITCH_BASE) {
|
||||||
int sample = resampleBuffer[sampleFrac / PSP_SAS_PITCH_BASE + 2];
|
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;
|
sampleFrac += voice.pitch;
|
||||||
|
|
||||||
// The maximum envelope height (PSP_SAS_ENVELOPE_HEIGHT_MAX) is (1 << 30) - 1.
|
// 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
|
// 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
|
// Ideally, the shift right should be there too but for now I'm concerned about
|
||||||
// not overflowing.
|
// not overflowing.
|
||||||
mixBuffer[i * 2] += (sample * voice.volumeLeft ) >> 12;
|
mixBuffer[i * 2] += (sample * voice.volumeLeft) >> 12;
|
||||||
mixBuffer[i * 2 + 1] += (sample * voice.volumeRight) >> 12;
|
mixBuffer[i * 2 + 1] += (sample * voice.volumeRight) >> 12;
|
||||||
sendBuffer[i * 2] += sample * voice.effectLeft >> 12;
|
sendBuffer[i * 2] += sample * voice.effectLeft >> 12;
|
||||||
sendBuffer[i * 2 + 1] += sample * voice.effectRight >> 12;
|
sendBuffer[i * 2 + 1] += sample * voice.effectRight >> 12;
|
||||||
}
|
}
|
||||||
|
|
||||||
voice.sampleFrac = sampleFrac;
|
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())
|
if (voice.HaveSamplesEnded())
|
||||||
voice.envelope.End();
|
voice.envelope.End();
|
||||||
if (voice.envelope.HasEnded())
|
if (voice.envelope.HasEnded()) {
|
||||||
{
|
|
||||||
// NOTICE_LOG(SCESAS, "Hit end of envelope");
|
// NOTICE_LOG(SCESAS, "Hit end of envelope");
|
||||||
voice.playing = false;
|
voice.playing = false;
|
||||||
voice.on = false;
|
voice.on = false;
|
||||||
|
@ -711,8 +678,11 @@ void SasInstance::DoState(PointerWrap &p) {
|
||||||
if (sendBuffer != NULL && grainSize > 0) {
|
if (sendBuffer != NULL && grainSize > 0) {
|
||||||
p.DoArray(sendBuffer, grainSize * 2);
|
p.DoArray(sendBuffer, grainSize * 2);
|
||||||
}
|
}
|
||||||
if (resampleBuffer != NULL && grainSize > 0) {
|
if (sendBuffer != NULL && grainSize > 0) {
|
||||||
p.DoArray(resampleBuffer, grainSize * 4 + 3);
|
// 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;
|
int n = PSP_SAS_VOICES_MAX;
|
||||||
|
|
|
@ -289,8 +289,6 @@ public:
|
||||||
s16 *sendBufferDownsampled;
|
s16 *sendBufferDownsampled;
|
||||||
s16 *sendBufferProcessed;
|
s16 *sendBufferProcessed;
|
||||||
|
|
||||||
s16 *resampleBuffer;
|
|
||||||
|
|
||||||
FILE *audioDump;
|
FILE *audioDump;
|
||||||
|
|
||||||
void Mix(u32 outAddr, u32 inAddr = 0, int leftVol = 0, int rightVol = 0);
|
void Mix(u32 outAddr, u32 inAddr = 0, int leftVol = 0, int rightVol = 0);
|
||||||
|
|
Loading…
Add table
Reference in a new issue