1

I have an array of audio data, which is a lot of Int32 numbers represented by array of bytes (each 4 byte element represents an Int32) and i want to do some manipulation on the data (for example, add 10 to each Int32).

I converted the bytes to Int32, do the manipulation and convert it back to bytes as in this example:

//byte[] buffer;
for (int i=0; i<buffer.Length; i+=4)
{
    Int32 temp0 = BitConverter.ToInt32(buffer, i);
    temp0 += 10;
    byte[] temp1 = BitConverter.GetBytes(temp0);
    for (int j=0;j<4;j++)
    {
        buffer[i + j] = temp1[j];
    }
}

But I would like to know if there is a better way to do such manipulation.

10
  • 1
    If you know what order the bytes are in, you can just do the addition yourself directly on the bytes. Commented Dec 18, 2016 at 19:59
  • 2
    Define "better". Do you find this unreadable? Is it too slow? Commented Dec 18, 2016 at 19:59
  • I find this both ugly and slow, any suggestion that would emprove one would be more than appreciated. Commented Dec 18, 2016 at 20:58
  • @MillieSmith it's big endian if that what you meant, but i'm not sure that adding the bytes while taking care of the carry bit every time will be faster. Do you have better idea? Commented Dec 18, 2016 at 21:06
  • 2
    @pio If it's big endian, don't use the BitConverter class. The BitConverter class assumes your system's native endianness. Commented Dec 18, 2016 at 22:47

2 Answers 2

1

You can check the .NET Reference Source for pointers (grin) on how to convert from/to big endian.

class intFromBigEndianByteArray {
    public byte[] b;
    public int this[int i] {
        get {
            i <<= 2; // i *= 4; // optional
            return (int)b[i] << 24 | (int)b[i + 1] << 16 | (int)b[i + 2] << 8 | b[i + 3];
        }
        set {
            i <<= 2; // i *= 4; // optional
            b[i    ] = (byte)(value >> 24);
            b[i + 1] = (byte)(value >> 16);
            b[i + 2] = (byte)(value >>  8);
            b[i + 3] = (byte)value;
        }
    }
}

and sample use:

byte[] buffer = { 127, 255, 255, 255, 255, 255, 255, 255 };//big endian { int.MaxValue, -1 }

//bool check = BitConverter.IsLittleEndian;     // true
//int test = BitConverter.ToInt32(buffer, 0);   // -129 (incorrect because little endian)

var fakeIntBuffer = new intFromBigEndianByteArray() { b = buffer };

fakeIntBuffer[0] += 2;    // { 128, 0, 0, 1 } = big endian int.MinValue - 1
fakeIntBuffer[1] += 2;    // {   0, 0, 0, 1 } = big endian 1

Debug.Print(string.Join(", ", buffer)); // "128, 0, 0, 0, 1, 0, 0, 1" 

For better performance you can look into parallel processing and SIMD instructions - Using SSE in C# For even better performance, you can look into Utilizing the GPU with c#

Sign up to request clarification or add additional context in comments.

7 Comments

Thx! If i would like to do the same for little endian (byte array representing int32 in little endian), what should I change? Does the order of the >>/<< will be enough? (>>24 for i+3 instead for i and so on).
@pio changing just either the order of the bit shifts or the order of the indexes is enough, but for little endian on a little endian system you can just get a int reference pointer to the memory location in the byte array with unsafe code.
you mean something like: int [] converted = buffer;
@pio I mean that no conversion would be needed at all, because the byte array would contain the integer in the right byte order and just getting the memory address in the byte array would be enough. Kind of like in the .Net Reference source fixed( byte * pbyte = &buffer[startIndex]) { return *((int *) pbyte); }
@pio Sorry, that was kind of a bad advice as unsafe code and pointers are more for advanced users that understand it (even I don't use it), and BitConverter.ToInt32 already does it for you. For little endian bytes on a little endian architecture I would recommend BinaryReader.ReadInt32 and BinaryWriter.Write(Int32) that you can use over FileStream or MemoryStream
|
1

How about the following approach:

struct My
{
    public int Int;
}
var bytes = Enumerable.Range(0, 20).Select(n => (byte)(n + 240)).ToArray();
foreach (var b in bytes) Console.Write("{0,-4}", b);

// Pin the managed memory
GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);

for (int i = 0; i < bytes.Length; i += 4)
{
    // Copy the data
    My my = (My)Marshal.PtrToStructure<My>(handle.AddrOfPinnedObject() + i);

    my.Int += 10;

    // Copy back
    Marshal.StructureToPtr(my, handle.AddrOfPinnedObject() + i, true);
}
// Unpin
handle.Free();

foreach (var b in bytes) Console.Write("{0,-4}", b);

I made it just for fun.

Not sure that's less ugly.

I don't know, will it be faster? Test it.

3 Comments

@Phil1970 - I specifically used these values to show that the code correctly makes the carry propagation.
Well, you didn't choose meaningful name for My so by the time I realize that (it was holding an integer), my comment was posted but I deleted it as soon as I realize it.
I think that you have a lot of overhead by having AddrOfPinnedObject inside the loop. Maybe, you have to do that if you want "safe" code. Otherwise, How to: Use Pointers to Copy an Array of Bytes might be useful.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.