davy7125 / polyphone

A soundfont editor for quickly designing musical instruments.
https://www.polyphone.io
GNU General Public License v3.0
348 stars 46 forks source link

[ENCHANCEMENT] Rework SF3 Logic To Preserve Compressed Samples #204

Open spessasus opened 1 month ago

spessasus commented 1 month ago

The Issue

SF3 uses Ogg Vorbis compression, which is lossy. This poses a problem because loading an SF3 into Polyphone and saving it again decompresses and then recompresses the samples, leading to a loss in quality.

The Solution

I propose that Polyphone should preserve compressed samples instead of decompressing them. With issue #201 now fixed, here’s how the logic could be adjusted:

Loading an .sf3 Soundfont

  1. Load the Soundfont: Load the soundfont with compressed samples, but decompress them only into memory for playback.
  2. Adding Samples: When a user adds a sample, save it as uncompressed. SF3 format supports a mix of compressed and uncompressed samples so this shouldn't be a problem.
  3. Saving the Soundfont: When saving, keep the compressed samples unchanged and save the new uncompressed samples as is. The exported format will be .sf3. To export an .sf2, user should use the export menu which is described below.

Exporting an .sf3 Soundfont

When Exporting .sf3

When Exporting .sf2

This approach avoids unnecessary decompression and recompression, thus preserving sample quality.

Let me know what you think!

davy7125 commented 1 month ago

I will delay a bit the work on the sf2 / sf3 parser since I need more time to do and test it:

When clicking on a sample in the tree, I'll also add a checkbox (which will be also a warning) so that we can decide if we want the compressed or raw version of the waveform.

spessasus commented 1 month ago

Hi Davy,

I have a slightly different idea on handling the samples. Instead of a modified attribute, I think a sample would have a compressed boolean. And two data arrays: compressed data and uncompressed data. When polyphone loads the soundfont, it loads the compressed data and decompresses it into another table and uses it for playback. For the uncompressed samples, the compressed data table is empty.

When I open a sample in Polyphone, it could show at the bottom "sample type: compressed" or something like that. It would not decompress the sample.

Editing behavior

Once the user clicks yes, the compressed flag is set to false and the compressed array gets deleted, leaving only the uncompressed array.

When saving the soundfont, simply check for the compressed boolean. If true, copy the compressed data (and adjust the sample index and loop points) and add the compressed flag to sampleType. If the boolean is false, copy the uncompressed data and save as an usual sf2 sample (and always zero the 4th bit of sampleType because the sample might've been compressed)

I've implemented this logic in my soundfont code

This function returns the "raw data" which gets directly copied into the file. As you can see, it copies the compressed data if the sample is compressed and uncompressed data otherwise.

Let me know what you think about my version and thanks for considering this feature!