1

I work with Media Foundataion and what I need to do is convert sound sample frame from byte to audio float data. In order to do it I use such method (that I found somewhere at google):

    private static float[] Convert16BitByteArrayToAudioClipData(byte[] source, int headerOffset, int dataSize)
    {
        int wavSize = BitConverter.ToInt32(source, headerOffset);
        headerOffset += sizeof(int);
        Debug.AssertFormat(wavSize > 0 && wavSize == dataSize, "Failed to get valid 16-bit wav size: {0} from data bytes: {1} at offset: {2}", wavSize, dataSize, headerOffset);

        int x = sizeof(Int16); // block size = 2
        int convertedSize = wavSize / x;

        float[] data = new float[convertedSize];

        Int16 maxValue = Int16.MaxValue;
        int i = 0;

        while (i < convertedSize)
        {
            int offset = i * x + headerOffset;
            data[i] = (float)BitConverter.ToInt16(source, offset) / maxValue;
            ++i;
        }

        Debug.AssertFormat(data.Length == convertedSize, "AudioClip .wav data is wrong size: {0} == {1}", data.Length, convertedSize);

        return data;
    }

I use it like this :

...
byte[] source = ...; // lenght 43776

... = Convert16BitByteArrayToAudioClipData(source , 0, 0);
...

Looks like this method works wrong, because if I pass an array with size 43776 as a result in while loop at index i = 21886 offset value will be offset = 43776 it lead to exception at this next method

data[i] = (float)BitConverter.ToInt16(source /*43776*/, offset /*43776*/) / maxValue;

because this values could not be the same.

Question is - how to fix this method? Or maybe someone can advice what to use instead?

EDIT

    private static float[] Convert16BitByteArrayToAudioClipData(byte[] source)
    {
        float[] data = new float[source.Length];

        for (int i = 0; i < source.Length; i++)
        {
            data[i] = (float) source[i];
        }

        return data;
    }
Sirop4ik
  • 4,543
  • 2
  • 54
  • 121
  • I think the audio file has an ASCII header. Open with notepad. Usually there is a start character like 0x01 where the audio starts and the size. – jdweng Nov 26 '20 at 13:35
  • Your previous questions were with C++ code for Media Foundation. Did you really switch to C#? The code snippet makes no sense for processing supposed PCM audio data buffers. – Roman R. Nov 26 '20 at 13:44
  • @RomanR. I do. I work with Unity. With media foundation I decode file in order to get sample frames (win impl on android impl I have another decoder), then I pass them all to C# side, because Unity Audio Player written in C# in order to support cross-platform – Sirop4ik Nov 26 '20 at 13:48
  • Media Foundation API gives you (supposedly - your question does not hold any information on effective audio data format) compact array of signed 16-bit integers. The code snippet you attached unexpectedly derives size from the bitstream... this is clearly wrong and you just need to read ints one by one and convert them to floats. – Roman R. Nov 26 '20 at 13:52
  • @RomanR. edited my question. Is it what you mean? – Sirop4ik Nov 26 '20 at 13:57

2 Answers2

1

Integers need to become -1..+1 floating point values

    private static float[] Convert16BitByteArrayToAudioClipData(byte[] source)
    {
        float[] data = new float[source.Length];

        for (int i = 0; i < source.Length; i++)
        {
            data[i] = ((float) source[i] / Int16.MaxValue); // <<---
        }

        return data;
    }
Roman R.
  • 68,205
  • 6
  • 94
  • 158
  • This is wrong. If source is `16 bit`, the `data` array should be half length of the `source` array. Also `source[i] / Int16.MaxValue` will produce slightly incorrect values for negative values. – apocalypse Nov 26 '20 at 14:06
  • There is one valid aspect in what @apocalypse wrote above (just one): if this code suddenly worked for you then your bye array data is not 16-bit but 8-bit (which may be also possible since you don't do format checking at least in this code). If bytes hold 16-bit values then you need to change `source[i]` to proper reading of those values and also update array size respectively. You will have to find this out via debugging since what you shown in the question is insufficient on its own. – Roman R. Nov 26 '20 at 14:13
  • Just now I finished with final implementation and as a result I hear a lot of noise at the background along with sound. I tried to write my decoded bytes to `.wav` file and playback from win player and I hear that it is ok, so I am pretty sure that problem could be in convert method, but I don't know how to check it, any ideas? Thanks – Sirop4ik Dec 01 '20 at 12:50
  • @apocalypse can you suggest what is a possible way to fix it? Thanks – Sirop4ik Dec 01 '20 at 12:51
  • As mentioned above, you might need `BitConverter.ToInt16` to read 16-bit values rather than using operator `=` against byte array. – Roman R. Dec 01 '20 at 13:19
0

Eventually I did it this way:

    public static float[] Convert16BitByteArrayToAudioClipData(byte[] source)
    {
        int x = sizeof(Int16); 
        int convertedSize = source.Length / x;
        float[] data = new float[convertedSize];
        Int16 maxValue = Int16.MaxValue;

        for (int i = 0; i < convertedSize; i++)
        {
            int offset = i * x;
            data[i] = (float)BitConverter.ToInt16(source, offset) / maxValue;
            ++i;
        }

        return data;
    }
Sirop4ik
  • 4,543
  • 2
  • 54
  • 121