picogus icon indicating copy to clipboard operation
picogus copied to clipboard

Psuedocode for doing fast cubic spline interpolation (for better resampling quality)

Open 8bitbubsy opened this issue 2 years ago • 2 comments

So I saw the talk about a possible cubic spline interpolator mode for PicoGUS, which would result in crisper audio with less treble being cut off from linear interpolation.

Here's some code I wrote that should work: https://pastebin.com/raw/Yvyh63VP

The only problem is that you need to input the correct sample points for the s0..s3 function parameters. This is slightly tricker than having to deal with just one extra sample (like with linear interpolation).

Here's what s0..s3 should be based on a few conditions (could be off, but something like this anyway).

Some notes first:

  • Pos is the current integer sampling position
  • End is the end point for a non-looping sample
  • s is a pointer to the sample data (either 8-bit or 16-bit samples)
  • HasLooped is a flag for having reached the end of a loop on a looping sample. Set it to false when a voice is starting to play a sample (sample trigger), and set it to true when you have looped at least once (sample is looping its loop now). I'm sure it has to be set to false in other situations as well.

Non-looping sample:

  • s0 = if (Pos == 0) s[0] else s[Pos-1];
  • s1 = s[Pos]
  • s2 = if (Pos+1 >= End) s[End-1] else s[Pos+1]; (yes, End-1. As in the last sample point)
  • s3 = if (Pos+2 >= End) s[End-1] else s[Pos+2];

Forward-looping sample:

  • s0 = if (HasLooped) { if (Pos == LoopStart) s[LoopEnd-1] else s[Pos-1]; } else if (Pos == 0) s[0] else s[Pos-1]; }
  • s1 = s[Pos]
  • s2 = if (Pos+1 >= LoopEnd) s[LoopStart+((Pos+1)-LoopEnd)] else s[Pos+1];
  • s3 = if (Pos+2 >= LoopEnd) s[LoopStart+((Pos+2)-LoopEnd)] else s[Pos+2];

Bidirectional-looping sample:

  • s0 = if (HasLooped) { if (Pos == LoopStart) s[LoopStart+1] else s[Pos-1]; } else if (Pos == 0) s[0] else s[Pos-1]; }
  • s1 = s[Pos]
  • s2 = if (Pos+1 >= LoopEnd) s[LoopEnd-((Pos+2)-LoopEnd)] else s[Pos+1]; (do Pos+2/Pos+3 for an extra -1)
  • s3 = if (Pos+2 >= LoopEnd) s[LoopEnd-((Pos+3)-LoopEnd)] else s[Pos+2];

There could be some off-by-one (or bigger) errors here, so do experiment with it. If it's possible to sample backwards in "forward loop" mode, then it would also need more logic.

PS: I totally understand if this is too bothersome to implement, and that's fine. :-)

8bitbubsy avatar Dec 16 '22 22:12 8bitbubsy

Hey, I'm not part of this project, but I wonder what you think the performance / RAM impact of this will be :)

ramapcsx2 avatar Dec 17 '22 15:12 ramapcsx2

It ought to be quite faster than doing cubic spline without a table. RAM usage for the table is 4x512x2 (4096) bytes.

EDIT: Naturally it's slower than linear interpolation, but the quality improvement is quite good.

8bitbubsy avatar Dec 17 '22 15:12 8bitbubsy