SteveLillis / .NET-Ogg-Vorbis-Encoder

Ogg Vorbis audio encoding library written in C#
MIT License
69 stars 16 forks source link

Unsafe.As not available in Unity3D #18

Closed klauskobald closed 2 years ago

klauskobald commented 2 years ago

I got everything to work except for that function:

      public static float ToDecibel(this float x)
        {
            var i = Unsafe.As<float, uint>(ref x);
            i &= 0x7fffffff;
            return i * 7.17711438e-7f - 764.6161886f;
        }

I replaced it by simply return x; for testing. I got some kind of encoding but with crackles and overloads, which seems obvious, because the decibels are calculated wrongly.

Is there any kind of replacement with simple math I could do? I do not understand that code. It converts the float into a positve int or something. Values for x seem to be between 0 and something around 3000. So what is unsafe.as doing? And what about those weird constants?

I tried return x * 7.17711438e-7f - 764.6161886f; But that does not work.

klauskobald commented 2 years ago

I found a solution:

    public static class FloatExtensions
    {
        public static float ToDecibel(this float x)
        {
            uint y = BitConverter.ToUInt32(BitConverter.GetBytes(x), 0);
            return y * 7.17711438e-7f - 764.6161886f;
        }
    }

And now I understand. That´s a really smart way to calculate the logarithm!! It might not as optimized as your code though!

cabauman commented 2 years ago

Hello, will you please include details like which Unity version you're using, the API compatibility level setting, and platform you're building for? Unsafe code can be enabled like this.

ben-abraham commented 2 years ago

Neat trick! Reminds me of the quake fast-inverse-square-root trick, which effectively did the same thing. Use the floating point representation as a "free" hacky log-value.

Your suggested solution should work perfectly fine, and as it seems that's the only place Unsafe. is used in the project, it would completely remove that dependency.

cabauman commented 2 years ago

The original implementation used the BitConverter.ToUInt32(BitConverter.GetBytes(x), 0) strategy but I changed it because it is extremely inefficient. See my quote from the PR:

The biggest memory hog was FloatExtensions.ToDecibel. It's one of the most called methods in the entire project and it allocates a new 4 byte array every single time.

I tweeted about it at the time too: https://twitter.com/GameConstructor/status/1310402754976702464?s=20&t=hv8YevmCpLKBqGW51_dz0A

If it didn't make such a huge difference, I wouldn't have bothered adding that dependency. And as a bit of background, I was working with Unity when I made that performance PR. So I was more than willing to turn on that 'allow unsafe code' flag to improve performance. My memory is a bit rusty, so if enabling unsafe in Unity doesn't resolve the issue, it probably requires the System.Memory dll (and its netstandard2.0 dependent dlls such as System.Runtime.CompilerServices.Unsafe) to be added to the Unity project.

klauskobald commented 2 years ago

I am using a pretty recent version of Unity: 2021.3 Of course I turned on Allow unsafe Code. But the function unsafe.As() is not available.

image

I tried both Api Levels. (cannot find 4.x)

image
cabauman commented 2 years ago

Ok in that case, can you try what I mentioned in the last part of my previous message: adding the System.Runtime.CompilerServices.Unsafe dll to your plugins folder (downloadable from that NuGet link). I just tried and it's recognized after I add that dll.

klauskobald commented 2 years ago

hm - big fear! Will that dll work on iOs, Android, Windows, Mac (intel+M1), Apple TV? That´s basically the reason I try to build everything from source without dependencies.

klauskobald commented 2 years ago

DONE! To wrap this up - this is the solution without dll:

        unsafe public static float ToDecibel(this float x)
        {
            uint* y = (uint*)&x;
            (*y) &= 0x7fffffff;
            return (*y) * 7.17711438e-7f - 764.6161886f;
        }