Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolve int16 wt interpretation documentation and comparability #7694

Closed
abique opened this issue Jul 9, 2024 · 58 comments · Fixed by #7703
Closed

Resolve int16 wt interpretation documentation and comparability #7694

abique opened this issue Jul 9, 2024 · 58 comments · Fixed by #7703
Labels
Audio Formats Bug Report Item submitted using the Bug Report template

Comments

@abique
Copy link
Collaborator

abique commented Jul 9, 2024

Hi,

According to the specification, if you want to encode 16 bits samples, you need the flags = 0x0C while it currently sets flags = 0x04 which is for 15 bits samples.

Reference: https://github.com/surge-synthesizer/surge/blob/main/resources/data/wavetables/WT%20fileformat.txt#L11

When looking at https://github.com/surge-synthesizer/surge/blob/main/scripts/wt-tool/wt-tool.py#L43

The bug seems to be here:

flags["format"] = "int16" if (fl[0] & 0x04 != 0) else "float32"

Cheers,
Alex

@abique abique added the Bug Report Item submitted using the Bug Report template label Jul 9, 2024
@baconpaul baconpaul added this to the Surge XT 1.3.3 milestone Jul 9, 2024
@baconpaul
Copy link
Collaborator

Yeah this is a remnant from before the format supported full height 16

The full fix here is

  1. Retire the --norm flag
  2. add a "--int-as-15" flag
  3. If you set int as 15 then set to 0x04 else 0x0C
  4. If you set the int as 15 then >>1 the samples on the encoding

Sort of one of those "quick hours" so let me tag it into 1.3.3 so we remember

@mkruselj
Copy link
Collaborator

mkruselj commented Jul 9, 2024

@baconpaul IMO this basically means keep the "peak" normalization mode (not as an option but to use it when encoding) because it will make sure all waveforms are hitting that 2^15. Right?

EDIT: Actually... no. Normalization should still be an option - for some WTs it makes more sense to have all waveforms peak normed, for some it doesn't. What int-as-15 flag should make sure is just that things don't go over 2^15, UNLESS peak normalizing option is set, which would then drive through all frames and peak norm them.

@mkruselj
Copy link
Collaborator

mkruselj commented Jul 9, 2024

Also update the .wt format spec (especially that maximum frame length is 4096 now).

@abique
Copy link
Collaborator Author

abique commented Jul 9, 2024

I think here the spec could be a bit more explicit:

  • when in 15 bits mode, should the 16 bit be ignored (left shift by 1 or masked) or we should read the whole 16 bits as a short then normalize it accordingly to the flags
  • have a note regarding badly encoded files and how to detect it and how to decide to force the decoding as 16 bits

The goal is to have the same sound for everyone.

@baconpaul
Copy link
Collaborator

Lemme look at what we do in the surge engine and then we can compare notes. Will add it to my list for this week

@abique
Copy link
Collaborator Author

abique commented Jul 10, 2024

FYI, Bitwig was previously loading the 16 bits value into a short, then scaling accordingly to the flag then clipping to -1..1.
So Bitwig never ignored the MSB.

@abique
Copy link
Collaborator Author

abique commented Jul 10, 2024

For the mitigation of the wavetable I propose this:

int16_t MAX15 = 1 << 14;
int16_t IS16_THR = MAX15 + MAX15 / 4;
int16_t sample = ...;
if ((sample <= -IS16_THR) || (IS16_THR <= sample))
   setIs16Bits(true);

@abique
Copy link
Collaborator Author

abique commented Jul 10, 2024

Another option for the spec is to say: no mitigation, if the flags says 15 bits, then it is 15 bits and hard clip.

@abique
Copy link
Collaborator Author

abique commented Jul 10, 2024

Note for myself: 36445 (related issue number in Bitwig's bug tracker)

@baconpaul
Copy link
Collaborator

Here's what surge does today

if (this->flags & wtf_int16_is_16)

If we get full 16 bits flag we pre-process the int with i16toi15_block and then call i152float_block in either case

inline void i16toi15_block(short *s, short *o, int n)

i16toi15_block basically does a >>1 so in 16 bit mode we drop the LSB and go to 15 bit

https://github.com/surge-synthesizer/surge/blob/4fce6dd641758ddfac055f301cc822a243a30af6/src/common/dsp/vembertech/basic_dsp.h#L48C13-L48C28

i152float_block converts to float by multiplying by const float scale = 1.f / 16384.f;, or 2^14

Except for the i16to15 addition this is all still claes-era code.

We never check if the i15 inbound has bit 16 set. But the >>1 will make sure i16 inbound doesnt

Reading this now, a more accurate approach would be to have an i162float method and then call that where we dont shift and instead divide by 32768. We have very few 16 bit wavetables in the code base and this is a 1 bit difference (the amplitude would be the same since we move the >>1 from the integer to the float denom).

I propose the following then

  1. Surge modifies the wavetable load to instead of drop the LSB and multiply by 2, don't do that for int16
  2. Surge stays unchanged for int15. So int15 signals with an overflow will clip still.
  3. I'll change wt-tool.py to allow an -int16 flag which allows you to create an int16 and does no normalization

and that seems to be it right?

@mkruselj
Copy link
Collaborator

I'd keep the --peak flag (rename it to --norm) that would simply peak normalize all frames, optionally.

@baconpaul
Copy link
Collaborator

Right normalize them to max is +/-2^14 in int15 leave 16 unchanged? Or do you want to re-peak the 16 also so the highest value is 2^15

@mkruselj
Copy link
Collaborator

mkruselj commented Jul 10, 2024

Repeak in both cases to the max value (0 dBFS or -6 dBFS)

@baconpaul
Copy link
Collaborator

Gotcha.

So -i15 no repeak might clip
-i16 no repeak might have max < 2^15
-i15 and -i16 repeak will lower or raise

I think we want also a -reduce16to15 which just does a <<1 and leaves it alone. But that's an easy mode to add as well.

@abique
Copy link
Collaborator Author

abique commented Jul 10, 2024

My advice for wt-tool.py would be to remove the option to encode to 15 bits.

@baconpaul
Copy link
Collaborator

Oh that does solve all the problems right

duh me, good idea Alex

@baconpaul
Copy link
Collaborator

Another option for the spec is to say: no mitigation, if the flags says 15 bits, then it is 15 bits and hard clip.

I agree with this

@baconpaul
Copy link
Collaborator

Ugh this turns out to be a bit uglier to fix for a variety of bad choices in surge. Those choices are

  1. The patch always streams the wavetable as int16
  2. The window oscillator expects the 15 not 16 bit version
  3. So right now when we load we load as 15 and then save as 15
  4. which means if we don't scale each load save roundtrip cuts us by a factor of two
  5. and multiplying by 2 in that case when we save just destroys the bit

so this one won't be a 'this afternoon' thing. I think what I will need to do is preserve the original wavetable data to stream it into the patch. A bit tricky.

@baconpaul
Copy link
Collaborator

Wow turns out surge at head does the same today if you load a 16-as-16 wavetable. I guess no-one has been importing the bitwig factory wavetables into surge! This definitely needs a fix. Ugh. Probably the way to do it is to preserve the data and then patch the window oscillator

baconpaul added a commit to baconpaul/surge that referenced this issue Jul 10, 2024
wt files with full-range 16 bit ints (so 16-is-16 or flag
0x8 + 0x4 = 0xC) was broken in a couple of ways, but most
importnatly, didn't survive a save/restore roundtrip.

This fixes it in three ways

1. When converting 16-as-16 to float scale accordingly with
   a different function and leave the ints untouched
2. Modify the window oscillator so if it gets a 16-as-16 it
   reduces the height of the ints to be consistent with a
   15 with an extra shift, making I15 WT vs Window and
   I16 WT vs Window consistent
3. Make sure it streams properly. No change required but
   tested.

Addresses surge-synthesizer#7694
baconpaul added a commit to baconpaul/surge that referenced this issue Jul 10, 2024
wt files with full-range 16 bit ints (so 16-is-16 or flag
0x8 + 0x4 = 0xC) was broken in a couple of ways, but most
importnatly, didn't survive a save/restore roundtrip.

This fixes it in three ways

1. When converting 16-as-16 to float scale accordingly with
   a different function and leave the ints untouched
2. Modify the window oscillator so if it gets a 16-as-16 it
   reduces the height of the ints to be consistent with a
   15 with an extra shift, making I15 WT vs Window and
   I16 WT vs Window consistent
3. Make sure it streams properly. No change required but
   tested.

Addresses surge-synthesizer#7694

Update wt-tool to allow int16 and int15 options both
@baconpaul
Copy link
Collaborator

OK I got it all dealt with, including making the options to wt-tool more rational and testing them fully. Will merge shortly.

@abique
Copy link
Collaborator Author

abique commented Jul 10, 2024

That was fast!
If you do a PR I can review it.

@baconpaul
Copy link
Collaborator

#7703

baconpaul added a commit that referenced this issue Jul 10, 2024
wt files with full-range 16 bit ints (so 16-is-16 or flag
0x8 + 0x4 = 0xC) was broken in a couple of ways, but most
importnatly, didn't survive a save/restore roundtrip.

This fixes it in three ways

1. When converting 16-as-16 to float scale accordingly with
   a different function and leave the ints untouched
2. Modify the window oscillator so if it gets a 16-as-16 it
   reduces the height of the ints to be consistent with a
   15 with an extra shift, making I15 WT vs Window and
   I16 WT vs Window consistent
3. Make sure it streams properly. No change required but
   tested.

Addresses #7694

Update wt-tool to allow int16 and int15 options both
@abique
Copy link
Collaborator Author

abique commented Jul 11, 2024

Yeah, as of now I feel that it is the best option.

Let's take a moment to think about it? I'll also adjust Bitwig's code and try it.

@mkruselj
Copy link
Collaborator

“int15 means peal values of 2^14 are 0db signals rather than 2^15"

Doesn't this change the sound of existing patches?

@abique
Copy link
Collaborator Author

abique commented Jul 11, 2024

“int15 means peal values of 2^14 are 0db signals rather than 2^15"

Doesn't this change the sound of existing patches?

Actually it preserves it. In bitwig we would have played with up to +6dB already and maybe surge too?

@mkruselj
Copy link
Collaborator

I'm fine with that spec, then!

@baconpaul
Copy link
Collaborator

Yeah the nice thing about what I wrote is it is actually what surge does after my push

I’m off to the dentist now but let me add that sentence to the spec and then maybe cherry pick it and one or two other things into a surge and surge rack point release yeah!

@abique
Copy link
Collaborator Author

abique commented Jul 11, 2024

Yeah the nice thing about what I wrote is it is actually what surge does after my push

I’m off to the dentist now but let me add that sentence to the spec and then maybe cherry pick it and one or two other things into a surge and surge rack point release yeah!

If you don't mind waiting a bit before racking a point release, I'll need a bit of time but I'd like to check this with Claes as well. (we're very busy atm).

@abique
Copy link
Collaborator Author

abique commented Jul 11, 2024

By the way the spec doesn't mention if the cycle size has to be a power of 2.
If it isn't mandatory maybe it could be mentioned that the cycle size is an arbitrary number?

@baconpaul
Copy link
Collaborator

It had to be power of 2 in surge for a variety of reasons and think that’s the case in serum and vital also. May be a good thing to put in spec

I’ll reopen this and change the title - and no rush on a point release but would be nice for surge and rack to have working access to their bitwig wt content properly

@baconpaul baconpaul reopened this Jul 11, 2024
@baconpaul baconpaul changed the title Possible issue in wt-tool.py when encoding 16 bits wavefiles Resolve int16 wt interpretation documentation and comparability Jul 11, 2024
@abique
Copy link
Collaborator Author

abique commented Jul 11, 2024

would be nice for surge and rack to have working access to their bitwig wt content properly

You can install all bitwig packs, then right click on a wavetable -> reveal file and you'll find them. The folder structure is quite simple, so go down a few directories and you can find all *.wt files.

We have wavetables spread over a few packs I think.

@mkruselj
Copy link
Collaborator

I wouldn't mind if BWS offers a pack that is all WTs only (merging WTs from disparate packs). :)

@baconpaul
Copy link
Collaborator

would be nice for surge and rack to have working access to their bitwig wt content properly

You can install all bitwig packs, then right click on a wavetable -> reveal file and you'll find them. The folder structure is quite simple, so go down a few directories and you can find all *.wt files.

We have wavetables spread over a few packs I think.

Yeah this is how I found one to test and found the bug I fixed yesterday

I’m debating modifying surge storage wavetable scans to look for the bitwig install locations and add them to the menu …. I know the location in Mac but don’t suppose you can share where the packs install win lin could you?

@abique
Copy link
Collaborator Author

abique commented Jul 11, 2024

This can be overridden by the user: image

The location on Linux is $HOME/.BitwigStudio/installed-packages/.
I'm not sure for Windows, I'll let you know once I boot Windows.

@mkruselj
Copy link
Collaborator

I guess it should read the BWS prefs file to get the user overriden path, too, then.

@abique
Copy link
Collaborator Author

abique commented Jul 11, 2024

I guess it should read the BWS prefs file to get the user overriden path, too, then.

I think it is a bad idea. But you're free to try.
Also Bitwig may change its preference file format anytime.

You could try the standard locations, if they exists show them to the user. Then let the user add additional wavetables locations in Surge's settings.

@baconpaul
Copy link
Collaborator

I agree alex. If the user wants additional things they can use soft links to our search paths today. I was just thinking of the "I haven't screwed with my settings lets just make it work magically"

@abique
Copy link
Collaborator Author

abique commented Jul 12, 2024

I've implemented the discussed change in bitwig (allow the +6dB) it'll be in the next beta release.

@baconpaul
Copy link
Collaborator

OK great. Did you happen to see the windows wavetable path?

@abique
Copy link
Collaborator Author

abique commented Jul 17, 2024

OK great. Did you happen to see the windows wavetable path?

C:\Users\bique\AppData\Local\Bitwig Studio\installed-packages

@abique
Copy link
Collaborator Author

abique commented Jul 17, 2024

It seems that the specification file for the .wt file format hasn't been entirely updated yet, it is missing:

  • wave_size can go up to 4K now
  • wave_size MUST be a power of two

@mkruselj
Copy link
Collaborator

Updating it!

@baconpaul
Copy link
Collaborator

Ahh I was going to do this along with the bitwig paths once you shared the bitwig resource path on windows with me Alex! Do you happen to have that? Or I can ask a bitwig windows user if it’s not easy to see from the code

@abique
Copy link
Collaborator Author

abique commented Jul 17, 2024

Ahh I was going to do this along with the bitwig paths once you shared the bitwig resource path on windows with me Alex! Do you happen to have that? Or I can ask a bitwig windows user if it’s not easy to see from the code

C:\Users\bique\AppData\Local\Bitwig Studio\installed-packages

@baconpaul
Copy link
Collaborator

Ahh and see it is here now. Cool let’s leave this open until I modify storage

@baconpaul
Copy link
Collaborator

OK to avoid confusion I opened that up as a separate FR in #7720 and will now close this as done. Good stuff!

baconpaul added a commit to baconpaul/surge that referenced this issue Aug 8, 2024
…esizer#7703)

wt files with full-range 16 bit ints (so 16-is-16 or flag
0x8 + 0x4 = 0xC) was broken in a couple of ways, but most
importnatly, didn't survive a save/restore roundtrip.

This fixes it in three ways

1. When converting 16-as-16 to float scale accordingly with
   a different function and leave the ints untouched
2. Modify the window oscillator so if it gets a 16-as-16 it
   reduces the height of the ints to be consistent with a
   15 with an extra shift, making I15 WT vs Window and
   I16 WT vs Window consistent
3. Make sure it streams properly. No change required but
   tested.

Addresses surge-synthesizer#7694

Update wt-tool to allow int16 and int15 options both
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Audio Formats Bug Report Item submitted using the Bug Report template
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants