Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
    Todd
    @ToddBertaOldham
    And thanks for teaching me a bit about how the containers and decoding are setup.
    Stephan Vedder
    @feliwir
    Np :D I am doing the audio stuff for OpenSage if you are interested in taking a look: https://github.com/OpenSAGE/OpenSAGE
    Stephan Vedder
    @feliwir
    Noooo
    NVorbis only supports reading float samples? :(
    Stephan Vedder
    @feliwir
    @ToddBertaOldham are you working on anything of that sort?
    Support for int8 and int16?
    Todd
    @ToddBertaOldham
    8 bit and 16 bit files are supported. They are read into a 32 bit float PCM buffer and can then be converted into whatever format you want. This is done in the NVorbis test app, my own game engine, and some other audio libraries that use NVorbis. I convert into a 16 bit PCM format for XAudio2 like in the test app. https://github.com/ioctlLR/NVorbis/blob/master/OpenTKSupport/OggStream.cs#L438
    Stephan Vedder
    @feliwir
    I don't want 32bit floating PCM buffer though. It's a waste of memory & performance. NLayer (a similar lib to nvorbis for mp3) allows reading 16bit and 8bit diretly
    Todd
    @ToddBertaOldham
    I will agree that it sounds inefficient. I haven't had any issues personally though. At least with smaller files. It seems like this file would need to under go serious changes: https://github.com/ioctlLR/NVorbis/blob/master/NVorbis/VorbisStreamDecoder.cs.
    Todd
    @ToddBertaOldham
    Do we really know that this is an issue though? It's not the only ogg Vorbis library I've seen that only reads samples to a float buffer. I fully agree that it would be better to read to a 16 bit buffer directly but I'm limited on the amount of time I can put into NVorbis. You’re more knowledgeable in audio decoding than I am. If you want to start something, then I can try to find some time to help and then merge it, but I can't stop working on my game to work on something that might not even be problem. If it becomes one, then I’ll do all I can. I'm sorry.
    Slower loading times are not ideal but once the buffer is converted I imagine you won't be wasting memory or much performance on playing the sound.
    Stephan Vedder
    @feliwir
    It’s the first library i‘ve seen. The most popular sound playback library (OpenAL). Doesn’t even accept float samples. So why would it be the default format?
    I am streaming the audio data inside a game. So it’s a continuous waste of performance😄
    Todd
    @ToddBertaOldham
    It's the default format because it guarantees that all ogg files can be read without loss. I think the question is not whether there is a performance loss but how much of one? Is it worth writing new code to save what could be an almost irrelevant amount of time? I need to implement streaming into my engine at some point so if you prove to me that this is an issue then I will gladly try to fix it.
    Stephan Vedder
    @feliwir
    It’s not just the performance. Everyone has exactly the same problem. Should everyone write his own conversion code?
    I can do the conversion myself for now, but the next guy that wants to feel the stream in any audio library will have the same issue
    Todd
    @ToddBertaOldham
    Fair enough. Adding a conversion utility method directly into the library or creating a 16 bit read samples method that does the conversion for you would be easy. Actually reading in 16 bit would require more work.
    Todd
    @ToddBertaOldham
    And again I'm not against trying to implement reading in 16 bit I'm just limited on time and want to make sure that it would be worth it.
    Todd
    @ToddBertaOldham
    Since I need to implement streaming anyway, I'll try to do so this week and measure the overhead of casting.
    Todd
    @ToddBertaOldham
    After a bit more research I found that reading only a float buffer and then converting is very common.
    The official ogg implementation's method for loading uses a float buffer: https://github.com/xiph/vorbis/blob/ea8b03fce93444cb3cf0131909e15b4f8856e863/include/vorbis/codec.h#L211
    The official higher level ogg implementation converts the float data:
    https://github.com/xiph/vorbis/blob/master/lib/vorbisfile.c#L1963
    Here is that first method being used to load WebM audio (which is vorbis) in the Godot engine. The float buffer is then converted to a 16 bit int buffer: https://github.com/godotengine/godot/blob/4f5a7ebaecfcf00cf1e5c4af4b20034f0dcecd29/thirdparty/libsimplewebm/OpusVorbisDecoder.cpp#L90
    Todd
    @ToddBertaOldham
    Here is also the code used to load standalone ogg data. It has a method for a 16 bit int buffer but all it does is convert the float buffer. https://github.com/godotengine/godot/blob/4f5a7ebaecfcf00cf1e5c4af4b20034f0dcecd29/thirdparty/misc/stb_vorbis.c#L5210
    Stephan Vedder
    @feliwir
    @ToddBertaOldham see what i mean:
            public override long GetSamples(int samples, out byte[] data)
            {
                int bytes = _audioFormat.BytesPerSample * samples;
                data = new byte[bytes];
                var readBuf = new float[samples];
                _reader.ReadSamples(readBuf, 0, samples);
    
                CastBuffer(readBuf, data, samples);
    
                return samples;
            }
    two rather large allocations in one function
    Todd
    @ToddBertaOldham
    For streaming purposes you would reuse both buffers. You wouldn't reallocate them.
    https://github.com/ioctlLR/NVorbis/blob/master/OpenTKSupport/OggStream.cs#L330
    Stephan Vedder
    @feliwir
    I know, but my api currently only supports that api :D
    Do you know if Array.Resize skips any allocation if the buffer size to subsequent calls is the same?
    Todd
    @ToddBertaOldham
    Are you using an 8 bit audio format?
    Stephan Vedder
    @feliwir
    16bit
    I support both 8 & 16 bit
    Todd
    @ToddBertaOldham
    Okay and I'm pretty sure Array.Resize will skip if the size is the same.
    "If newSize is greater than the Length of the old array, a new array is allocated and all the elements are copied from the old array to the new one. If newSize is less than the Length of the old array, a new array is allocated and elements are copied from the old array to the new one until the new one is filled; the rest of the elements in the old array are ignored. If newSize is equal to the Length of the old array, this method does nothing."
    Todd
    @ToddBertaOldham
    Well I hope this works out alright for you. I'm quite confident it will if you manage the allocations since casting seems to be the official way of doing this. I'm going to try and get audio streaming into my engine this week and see what happens. No promises though. I am considering adding a utility class to cast the samples. I think that might be a little out of scope of the project, but I agree that everyone using the library shouldn't need their own casting method.
    Stephan Vedder
    @feliwir
    I think that should work for me, thanls
    I'll try to switch to Span<T> to avoid even more allocations
    Todd
    @ToddBertaOldham
    Absolute worst-case scenario we can try and modify the library, but it looks like that'd be a difficult change. Especially when trying to keep it clean and not break compatibility. It's worth mentioning though that Concentus seems to decode only to short[]. Decoding to float[] is just casting short[] at a loss and byte[] doesn't seem to be supported without a custom casting method. I just skimmed through the decoding code, so I may be wrong, but the creator seems to have oddly done the opposite of everyone else.
    Stephan Vedder
    @feliwir
    Casting to short is only a loss of precision if the audio had a higher bit depth. I think most audio files don't
    Todd
    @ToddBertaOldham
    I agree that short[] is going to be the majority of cases in games, but I don't think it’s unreasonable to think that some projects need high precision audio.
    Stephan Vedder
    @feliwir
    Yes some might need it. But when the common audio libraries can only playback byte/short it should atleast be supported in some way
    Todd
    @ToddBertaOldham
    I agree. Which is why I'm considering adding conversion methods.
    Stephan Vedder
    @feliwir
    That would be awesome
    Todd
    @ToddBertaOldham
    Option 1 would go right on the Vorbis reader class. I'm not sure how I feel about this since it hides an extra allocation.
    int ReadSamples(short[] buffer, int offset, int count);
    int ReadSamples(short[] buffer, int offset, int count, float[] converstionBuffer);
    int ReadSamples(int[] buffer, int offset, int count);
    int ReadSamples(int[] buffer, int offset, int count, float[] converstionBuffer);
    int ReadSamples(byte[] buffer, int offset, int count);
    int ReadSamples(byte[] buffer, int offset, int count, float[] converstionBuffer);
    Option 2 would be a static class named along the lines of VorbisFormatUtility. I don't really like the idea of just throwing in some new static class.
    void ConvertPCM(float[] source, short[] destination, int length);
    void ConvertPCM(float[] source, int[] destination, int length);
    void ConvertPCM(float[] source, byte[] destination, int length);
    Names are not final.
    Todd
    @ToddBertaOldham
    Also ignore the spelling mistake. conversionBuffer is what I meant to write.
    I think I'm leaning more towards option 1 but maybe only use the overload with conversionBuffer .