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.

Millie Smith
  • 4,536
  • 2
  • 24
  • 60
pio
  • 500
  • 5
  • 12
  • 1
    If you know what order the bytes are in, you can just do the addition yourself directly on the bytes. – Millie Smith Dec 18 '16 at 19:59
  • 2
    Define "better". Do you find this unreadable? Is it too slow? – Cody Gray - on strike Dec 18 '16 at 19:59
  • I find this both ugly and slow, any suggestion that would emprove one would be more than appreciated. – pio Dec 18 '16 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? – pio Dec 18 '16 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. –  Dec 18 '16 at 22:47
  • If you works with audio, then you probably also want to ensure you do get any overflow as digital clipping can be very apparent in audio. – Phil1970 Dec 18 '16 at 23:23
  • @pio Yeah, I was talking about endianness. There's only one way to find out for sure. Time them and see which one is faster. – Millie Smith Dec 18 '16 at 23:39
  • @hvd thx! i'll check again to see if i can make it into little endian. – pio Dec 19 '16 at 08:21
  • @MillieSmith byte + byte is not supported in C# :( (it cast it automaticaly to ints), see this [link](http://stackoverflow.com/questions/941584/byte-byte-int-why). – pio Dec 19 '16 at 08:53
  • That's fine. It's just some casts. You won't know what's faster until you profile. – Millie Smith Dec 19 '16 at 08:56

2 Answers2

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.

Alexander Petrov
  • 13,457
  • 2
  • 20
  • 49
  • @Phil1970 - I specifically used these values to show that the code correctly makes the carry propagation. – Alexander Petrov Dec 18 '16 at 23:16
  • 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. – Phil1970 Dec 18 '16 at 23:19
  • 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](https://msdn.microsoft.com/en-us/library/28k1s2k6.aspx) might be useful. – Phil1970 Dec 18 '16 at 23:22
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#

Community
  • 1
  • 1
Slai
  • 22,144
  • 5
  • 45
  • 53
  • 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 Dec 19 '16 at 09:07
  • 1
    @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. – Slai Dec 19 '16 at 10:22
  • you mean something like: int [] converted = buffer; – pio Dec 19 '16 at 10:35
  • 1
    @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); }` – Slai Dec 19 '16 at 10:46
  • I tried this function: public static int* Convert(byte* pbyte ) { return (int*)pbyte; } but didnt succeed, I always get error regarding the convert of the byte[] to int*, can you help me write it correctly? I'm noobie in C# :( – pio Dec 19 '16 at 11:05
  • 1
    @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` – Slai Dec 19 '16 at 13:56
  • Thx for helping me, but I tried to use the BinaryReader but I didnt manage to do it on the byte array, it seems that it only work for IO according to [this documentation](https://msdn.microsoft.com/en-us/library/system.io.binaryreader(v=vs.110).aspx) – pio Dec 19 '16 at 16:29