ptitSeb
Serial Porter
Here is an experimental build. I tried to implement some Neon optimisation, in the sound mixing code. It may be faster or not, in Trail Blazer for exemple, that uses that (the SasAudio). I uses GCC Intrinsec, so I'm not sure my code ends up faster or slower than default gcc optimized one.
Here is the PND (same version as above, but with optims). PPSSPP.pnd
And here are the code modification. It's in the file "SasAudio.cpp"
Here is the PND (same version as above, but with optims). PPSSPP.pnd
And here are the code modification. It's in the file "SasAudio.cpp"
in the beggining, I put
#ifdef __ARM_NEON__
#include <arm_neon.h>
#endif
to be able to use intrinsec. Than, I modified, for now, only one function:
#ifdef __ARM_NEON__
#include <arm_neon.h>
#endif
to be able to use intrinsec. Than, I modified, for now, only one function:
Code:
void SasInstance::Mix(u32 outAddr) {
int voicesPlayingCount = 0;
for (int v = 0; v < PSP_SAS_VOICES_MAX; v++) {
SasVoice &voice = voices[v];
if (!voice.playing || voice.paused)
continue;
voicesPlayingCount++;
// TODO: Special case no-resample case for speed
if (voice.type == VOICETYPE_VAG && voice.vagAddr != 0) {
// 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
u32 numSamples = (voice.sampleFrac + grainSize * voice.pitch) / PSP_SAS_PITCH_BASE;
if ((int)numSamples > grainSize * 4) {
ERROR_LOG(SAS, "numSamples too large, clamping: %i vs %i", numSamples, grainSize * 4);
numSamples = grainSize * 4;
}
// Read N samples into the resample buffer. Could do either PCM or VAG here.
voice.vag.GetSamples(resampleBuffer + 2, numSamples);
// Smoothness HACKERY
resampleBuffer[2 + numSamples] = resampleBuffer[2 + numSamples - 1];
if (voice.vag.End()) {
// NOTICE_LOG(SAS, "Hit end of VAG audio");
voice.playing = false;
voice.on = false; // ??
}
// 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.
u32 sampleFrac = voice.sampleFrac;
#ifdef __ARM_NEON__
int32_t* mixBuf = mixBuffer;
int32_t* sendBuf = sendBuffer;
union {int32x2_t voiceVolx2; int32 voiceVol[2];};
union {int32x2_t voiceVolSendx2; int32 voiceVolSend[2];};
voiceVol[0]=voice.volumeLeft; voiceVol[1]=voice.volumeRight;
voiceVolSend[0]=voice.volumeLeftSend; voiceVolSend[1]=voice.volumeRightSend;
#endif
for (int i = 0; i < grainSize; i++) {
// For now: nearest neighbour, not even using the resample history at all.
#ifdef __ARM_NEON__
int32x2_t sample = vdup_n_s32(resampleBuffer[sampleFrac / PSP_SAS_PITCH_BASE + 2]);
#else
int sample = resampleBuffer[sampleFrac / PSP_SAS_PITCH_BASE + 2];
#endif
sampleFrac += voice.pitch;
// Reduce envelope to 15 bits, rounding down.
int envelopeValue = voice.envelope.GetHeight();
envelopeValue = ((envelopeValue >> 15) + 1) >> 1;
#ifdef __ARM_NEON__
sample = vshr_n_s32(vmul_n_s32(sample, envelopeValue), 15);
vst1_s32(mixBuf, vadd_s32(vld1_s32(mixBuf), vshr_n_s32(vmul_s32(sample, voiceVolx2), 12)));
vst1_s32(sendBuf, vadd_s32(vld1_s32(sendBuf), vshr_n_s32(vmul_s32(sample, voiceVolSendx2), 12)));
mixBuf += 2; sendBuf += 2;
#else
// We just scale by the envelope before we scale by volumes.
sample = sample * envelopeValue >> 15;
// 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 + 1] += sample * voice.volumeRight >> 12;
sendBuffer[i * 2] += sample * voice.volumeLeftSend >> 12;
sendBuffer[i * 2 + 1] += sample * voice.volumeRightSend >> 12;
#endif
voice.envelope.Step();
}
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.envelope.HasEnded())
{
// NOTICE_LOG(SAS, "Hit end of envelope");
voice.playing = false;
}
}
else if (voice.type == VOICETYPE_PCM && voice.pcmAddr != 0) {
resampleBuffer[0] = voice.resampleHist[0];
resampleBuffer[1] = voice.resampleHist[1];
u32 numSamples = voice.sampleFrac + grainSize ;
if ((int)numSamples > grainSize * 4) {
ERROR_LOG(SAS, "numSamples too large, clamping: %i vs %i", numSamples, grainSize * 4);
numSamples = grainSize * 4;
}
resampleBuffer[2 + numSamples] = resampleBuffer[2 + numSamples - 1];
voice.resampleHist[0] = resampleBuffer[2 + numSamples - 2];
voice.resampleHist[1] = resampleBuffer[2 + numSamples - 1];
u32 sampleFrac = voice.sampleFrac;
#ifdef __ARM_NEON__
int32_t* mixBuf = mixBuffer;
int32_t* sendBuf = sendBuffer;
union {int32x2_t voiceVolx2; int32 voiceVol[2];};
union {int32x2_t voiceVolSendx2; int32 voiceVolSend[2];};
voiceVol[0]=voice.volumeLeft; voiceVol[1]=voice.volumeRight;
voiceVolSend[0]=voice.volumeLeftSend; voiceVolSend[1]=voice.volumeRightSend;
#endif
for (int i = 0; i < grainSize; i++) {
int envelopeValue = voice.envelope.GetHeight();
envelopeValue = ((envelopeValue >> 15) + 1) >> 1;
#ifdef __ARM_NEON__
int32x2_t sample = vshr_n_s32(vmul_n_s32(vdup_n_s32(resampleBuffer[sampleFrac + 2]), envelopeValue), 15);
vst1_s32(mixBuf, vadd_s32(vld1_s32(mixBuf), vshr_n_s32(vmul_s32(sample, voiceVolx2), 15)));
vst1_s32(sendBuf, vadd_s32(vld1_s32(sendBuf), vshr_n_s32(vmul_s32(sample, voiceVolSendx2), 15)));
mixBuf += 2; sendBuf += 2;
#else
int sample = resampleBuffer[sampleFrac + 2];
sample = sample * envelopeValue >> 15;
mixBuffer[i * 2] += sample * voice.volumeLeft >> 15;
mixBuffer[i * 2 + 1] += sample * voice.volumeRight >> 15;
sendBuffer[i * 2] += sample * voice.volumeLeftSend >> 15;
sendBuffer[i * 2 + 1] += sample * voice.volumeRightSend >> 15;
#endif
voice.envelope.Step();
}
voice.sampleFrac = sampleFrac;
voice.sampleFrac -= numSamples ;
if (voice.envelope.HasEnded()) {
NOTICE_LOG(SAS, "Hit end of envelope");
voice.playing = false;
}
}
else if (voice.type == VOICETYPE_NOISE && voice.noiseFreq != 0) {
// Generate noise?
}
}
//if (voicesPlayingCount)
// NOTICE_LOG(SAS, "Sas mixed %i voices", voicesPlayingCount);
// Okay, apply effects processing to the Send buffer alone here.
// Reverb, echo, what have you.
// TODO
// Alright, all voices mixed. Let's convert and clip, and at the same time, wipe mixBuffer for next time. Could also dither.
#ifdef __ARM_NEON__
int32_t* mixBuf = mixBuffer;
int32_t* sendBuf = sendBuffer;
int32x2_t minInt=vdup_n_s32(-32768);
int32x2_t maxInt=vdup_n_s32(32767);
int32x2_t zeroInt=vdup_n_s32(0);
union {int32x2_t sampleLRx2; int32 sampleLR[2];};
#endif
for (int i = 0; i < grainSize; i++) {
#ifdef __ARM_NEON__
sampleLRx2 = vmax_s32(vmin_s32(vadd_s32(vld1_s32(mixBuffer), vld1_s32(sendBuf)), maxInt), minInt);
vst1_s32(mixBuf, zeroInt);
vst1_s32(sendBuf, zeroInt);
s16 outL, outR;
outL = sampleLR[0];
outR = sampleLR[1];
mixBuf+=2; sendBuf+=2;
#else
int sampleL = mixBuffer[i * 2] + sendBuffer[i * 2];
int sampleR = mixBuffer[i * 2 + 1] + sendBuffer[i * 2 + 1];
mixBuffer[i * 2] = 0;
mixBuffer[i * 2 + 1] = 0;
sendBuffer[i * 2] = 0;
sendBuffer[i * 2 + 1] = 0;
s16 outL, outR;
if (sampleL > 32767) outL = 32767;
else if (sampleL < -32768) outL = -32768;
else outL = sampleL;
if (sampleR > 32767) outR = 32767;
else if (sampleR < -32768) outR = -32768;
else outR = sampleR;
#endif
Memory::WriteUnchecked_U16(outL, outAddr + i * 2 * 2);
Memory::WriteUnchecked_U16(outR, outAddr + i * 2 * 2 + 2);
}
#ifdef AUDIO_TO_FILE
fwrite(Memory::GetPointer(outAddr), 1, grainSize * 2 * 2, audioDump);
#endif
}