Release PPSSPP, Emulator for PSP


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"

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:

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
}
 

Attachments

  • PPSSPP.pnd
    15.1 MB · Views: 245
I try this version this evening lately.


sound improvement is cool,then i understand the speech in tales of eternia little more clearly.


Tip,run PPSSPP from Mini Menu,then have you little Performance Boost too :)
 
So I've finally dumped Dissidia 012 Final Fantasy.

On the Windows Version I can enter battle.

On Pandora I can enter menu but I have no letters.

However I can move the courser and select stuff.

If I do so screen becomes white and that's it.

No crash but the game becomes white.

Is it because the JIT is better on x86?

(BTW: Menu is 60 FPS)

I tried the non modified version first.

Now I'll start with you version.

EDIT:

Didn't solve Dissida012 problem.

Can't tell much about sound, as I can't test it properly.
 
Last edited by a moderator:
I bought Dissidia too,but i cannot Dump in moment because my PSP is broken.


Cannot read out umds now :(


but will be repaired :)
 
Dissidia or Dissidia 012?

Get 012 as it also contains everything that's in first Dissidia.
 
I dont know exactly,i must see at Home. ;)

Yes its Dissidia 012 :D

Have tested some Games and missing some Sounds,speech is not faster/smoother.

Found a wonderfull Homebrew who work:

To all,try Super Mario Toy PSP as Iso,work fine,but scrambeld Sound.

The Sound in Tales of Eternia is little scambled too and Some Sounds are missing. ;)

But at all,the new Sound Plugin doesnt seem to be slower,

but some Things dont work fine who worked before ^_^
 
Last edited by a moderator:
So do you have the same problems with Dissidia 012 as I described above.

Want to get sure I didn't mess up some stuff.

Would be so awesome if Dissida 012 would be as playable as Trailblazer.

I also have Dantes inferno to test here.
 
Last edited by a moderator:
I must say sorry,i have the Game but no CSO or Iso from it,because my PSP is broken.

Then cannot test i it for you :unsure:
 
New Tales of Eternia Video is Uploading

http://youtu.be/8PwZpqs1emY

New Super Mario Toy Homebrew Game Video is uploading with Sound Errors/Scratches

I think they come from the Neon Optimized Plugin,never had such scratch Sound before.

But see the Video exactly,after the Backround Music was one Time Playing is the Sound most good.

http://youtu.be/zZyEcgIx_Mg

Uploading takes a while,have a bad Connection ;)
 
Last edited by a moderator:
On my Rebirth have i nearly the Same Speed but without Sound and overlocked to 1GHZ with OPP5,

wait i can make a Video with my Rebirth and show the Settings in the Video.

The Other Videos had Uploaded ;)   Enjoy ^_^

Edit:

Here the Video(is Uploading) on my rebirth with 1.0GHZ without Sound and started from Mini Menu ;)

http://youtu.be/7HdMVnj6LsM
 
Last edited by a moderator:
Hum, yes I may have messed up something trying to optimize. Anyway, this fille as also been optimized by the PPSSPP team, so my optims are obsolete, and are gone for now.

On Trail Blazer, you have 60fps on the 1 Ghz, I get 45 fps on my rebirth O/C at 1GHz.
 
Thx,i dont know if its Important but all Videos where made with the Latest SGX Drivers

4.08 or nearly Number :rolleyes:

It was the latest that i can install with Notaz Driver PND Package ^_^

Edit: I think i readed long Time before that the SGX from Rebirth can be overclocked too but

this is real Dangerous for the entire Unit.
 
Last edited by a moderator:
Got the pnd installed and put my ISO in the Home folder and nothing is shown when I try to select, am I missing something? Do ISOs work??
 
Got the pnd installed and put my ISO in the Home folder and nothing is shown when I try to select, am I missing something? Do ISOs work??
Yes, iso and cso both works. The home folder is the one from PPSSPP I assume? Look at the video from Ingoreis, you'll see .iso files as well.
 
Which folder do you put the ISOs in then to be able to select them?

thanks
 
You can put it anywhere. Either in the appdata/ppspp/home folder or on a "roms" folder in the root of your SDCard... You have to browse to that folder, using your finger, when doing "load..." button. It's an Android type of interface, so you press'n move to scroll, and press long without moving to select.

Hope it helps.
 
Back
Top