4

I have this code for converting a byte[] to float[].

public float[] ConvertByteToFloat(byte[] array)
{
    float[] floatArr = new float[array.Length / sizeof(float)];
    int index = 0;
    for (int i = 0; i < floatArr.Length; i++)
    {
        floatArr[i] = BitConverter.ToSingle(array, index);
        index += sizeof(float);
    }
    return floatArr;
}

Problem is, I usually get a NaN result! Why should this be? I checked if there is data in the byte[] and the data seems to be fine. If it helps, an example of the values are:

new byte[] {
    231,
    255,
    235,
    255,
}

But this returns NaN (Not a Number) after conversion to float. What could be the problem? Are there other better ways of converting byte[] to float[]? I am sure that the values read into the buffer are correct since I compared it with my other program (which performs amplification for a .wav file).

Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272
user488792
  • 1,943
  • 7
  • 31
  • 38
  • 2
    Is the data in the IEEE 754-1985 Binary Floating-Point Arithmetic format? Does the endianness agree with your system (most likely little-endian)? – Jeff Mercado Feb 20 '11 at 11:18
  • The endianess is one possible problem. If you reverse the order of those bytes it becomes a non-NaN float value. – Guffa Feb 20 '11 at 11:28
  • Audio is traditionally not stored as a sequence of floats, but a sequence of Int8 or Int16 samples. So are you sure the byte array contains floats? – CodesInChaos Feb 20 '11 at 11:30
  • @CodeInChaos: That is the more common formats, but storing samples as floats is also a supported format. IIRC it's even supported by regular soundcard hardware. – Guffa Feb 20 '11 at 11:32
  • @CodeInChaos Yes I think I read that somewhere. But Im using float[] since the algorithm I got for FFT asks for float[] input. – user488792 Feb 20 '11 at 11:32
  • If your input consists of Int8/16 samples you need to parse them as an int of the correct size and then convert each integer to a float(Typically by dividing by 2^8-1 or 2^16-1 and then adding 0.5f). – CodesInChaos Feb 20 '11 at 11:34
  • @CodeInChaos What are you doing to those poor Int8/Int16? Why are you adding 0.5f? – xanatos Feb 20 '11 at 12:11
  • @CodeInChaos Hi! I already have a code that converts bytes[] to short[]. But I am confused of the short to float conversion, because short = 2 bytes read and float = 4 bytes read. So Im not sure how to go about with the conversion. – user488792 Feb 20 '11 at 12:11
  • The conversion from `byte[]` to the raw samples in `Int8`/`Int16` form is a binary re-interpretation. The conversion from such a sample into float is a mathematical transformation unrelated to the binary representation. So the size of short and float don't come into play at all. – CodesInChaos Feb 20 '11 at 12:39

5 Answers5

4

If endianness is the problem, you should check the value of BitConverter.IsLittleEndian to determine if the bytes have to be reversed:

public static float[] ConvertByteToFloat(byte[] array) {
  float[] floatArr = new float[array.Length / 4];
  for (int i = 0; i < floatArr.Length; i++) {
    if (BitConverter.IsLittleEndian) {
      Array.Reverse(array, i * 4, 4);
    }
    floatArr[i] = BitConverter.ToSingle(array, i * 4);
  }
  return floatArr;
}
Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • I suspect this ultimately is the problem. Though it should be noted that this modifies the array so might not be the best approach. – Jeff Mercado Feb 20 '11 at 11:45
  • Hi and thanks! I tried with the IsLittleEndian approach and it does work! However, there are still values in the resulting float[] that have NaN values. What other problem could it be? – user488792 Feb 20 '11 at 11:56
2

Isn't the problem that the 255 in the exponent represents NaN (see Wikipedia in the exponent section), so you should get a NaN. Try changing the last 255 to something else...

Anders Zommarin
  • 7,094
  • 2
  • 25
  • 24
  • How is this relevant? Those numbers most likely came from a real float in that order. If converted as big-endian, it is a valid number (`-2.417114E+24`). You just can't change the data to get a real number. – Jeff Mercado Feb 20 '11 at 11:48
  • -2.41E+24 is a valid float, but typically not used in audio data, since audio data is typically restricted to -1 to 1. So I still think the byte array doesn't contain floats, but integer samples. – CodesInChaos Feb 20 '11 at 11:56
1

If it's the endianness that is wrong (you are reading big endian numbers) try these (be aware that they are "unsafe" so you have to check the unsafe flag in your project properties)

public static unsafe int ToInt32(byte[] value, int startIndex)
{
    fixed (byte* numRef = &value[startIndex])
    {
        var num = (uint)((numRef[0] << 0x18) | (numRef[1] << 0x10) | (numRef[2] << 0x8) | numRef[3]);
        return (int)num;
    }
}

public static unsafe float ToSingle(byte[] value, int startIndex)
{
    int val = ToInt32(value, startIndex);
    return *(float*)&val;
}
xanatos
  • 109,618
  • 12
  • 197
  • 280
  • If you need the funcs for doubles and Int64, just ask. I had to make them some months ago for a project of mine. – xanatos Feb 20 '11 at 11:29
  • Hi and thanks for this! It works although there are still some NaN values so I think that endianness is only 1 part of the problem – user488792 Feb 20 '11 at 12:12
  • @user488792 How can you know it works then? Do you have some "reference" numbers? – xanatos Feb 20 '11 at 12:15
  • I tried the other suggestion of one of the answers here, and I compared them to each other and they were (seemed to) be the same. – user488792 Feb 20 '11 at 12:40
1

I assume that your byte[] doesn't contain the binary representation of floats, but either a sequence of Int8s or Int16s. The examples you posted don't look like audio samples based on float (neither NaN nor -2.41E+24 are in the range -1 to 1. While some new audio formats might support floats outside that range, traditionally audio data consists of signed 8 or 16 bit integer samples.

Another thing you need to be aware of is that often the different channels are interleaved. For example it could contain the first sample for the left, then for the right channel, and then the second sample for both... So you need to separate the channels while parsing.

It's also possible, but uncommon that the samples are unsigned. In which case you need to remove the offset from the conversion functions.

So you first need to parse current position in the byte array into an Int8/16. And then convert that integer to a float in the range -1 to 1.

If the format is little endian you can use BitConverter. Another possibility that works with both endiannesses is getting two bytes manually and combining them with a bit-shift. I don't remember if little or big endian is common. So you need to try that yourself.

This can be done with functions like the following(I didn't test them):

float Int8ToFloat(Int8 i)
{
  return ((i-Int8.MinValue)*(1f/0xFF))-0.5f;
}

float Int16ToFloat(Int16 i)
{
  return ((i-Int16.MinValue)*(1f/0xFFFF))-0.5f;
}
CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
  • Hi! Thanks. Ill try to see if this works. Im only working with mono audio currently so I think thats one less problem. Im currently using BitConverter for my previous code so Ill try that first. – user488792 Feb 20 '11 at 12:44
0

Depends on how you want to convert the bytes to float. Start out by trying to pin-point what is actually stored in the byte array. Perhaps there is a file format specification? Other transformations in your code?

In the case each byte should be converted to a float between 0 and 255:

public float[] ConvertByteToFloat(byte[] array)
{
    return array.Select(b => (float)b).ToArray();
}

If the bytes array contains binary representation of floats, there are several representation and if the representation stored in your file does not match the c# language standard floating point representation (IEEE 754) weird things like this will happen.

vidstige
  • 12,492
  • 9
  • 66
  • 110