简体   繁体   中英

C# pointers on pointers

I have never used pointers before and now I need to and even though I've read up on them I still have no clue on how to use them! I am using a third party product (Mitov Signal Lab)and I am using a control that has an event that will allow me to intercept the data. When I enter the event I have access to "args" and one of the statements will return a pointer. Now this points to a buffer of 4096 double values (one a real number and the next an imaginiary number etc). So this is the code:

 private void genericComplex1_ProcessData(object Sender, Mitov.SignalLab.ProcessComplexNotifyArgs Args)
    {
        unsafe
        {
         IntPtr pointer =  Args.InBuffer.Read(); 

         // now what????  I want to get each double value, do some calculations on them
         // and return them to the buffer.
         }
      args.sendOutPutData = true;
   }

I'm assuming that i need to assign pointer to Args.InBuffer.Read() But I've tried some examples I've seen and I just get errors. I'm using visual studio 2010 and i'm compiling with the /unsafe. Here's an update. There are additional methods such as Args.Inbuffer.Write(), and Args.Inbuffer.Modify() but I still can't figure out how to access the individual components of the buffer.

Ok here are more details. I used the Marshall.copy. Here is the code:

   private void genericComplex1_ProcessData(object Sender, Mitov.SignalLab.ProcessComplexNotifyArgs Args)
    {
        //used to generate a phase corrected Q signal
        //equations:
        // real = real
        // imaginary = (1 + amplitudeimbalance)(real*cos(phaseImbalance) + imaginary*sin(phaseImbalance)
        //but amplitude imbalance is already corrected therefore

        //imaginary = real * cos(phaseCorrection) + imaginary*sin(phaseCorrection)

        double real;
        double imaginary;
        double newImaginary;

        var bufferSize = Convert.ToInt32(Args.InBuffer.GetSize());

        Mitov.SignalLab.ComplexBuffer ComplexDataBuffer = new Mitov.SignalLab.ComplexBuffer(bufferSize);
        ComplexDataBuffer.Zero();

        IntPtr pointer = Args.InBuffer.Read();
        IntPtr pointer2 = ComplexDataBuffer.Write();

        var data = new double[8192];

        Marshal.Copy(pointer, data, 0, 8192);

        for (int i = 0; i < 8192; i+=2)
        {
            data[i] = data[i];
            data[i + 1] = data[i] * Math.Cos(phaseCorrection) + data[i+1] * Math.Sin(phaseCorrection); 
        }

        Marshal.Copy(data, 0, pointer2, 8192);

       Args.SendOutputData = false;
       genericComplex1.SendData(ComplexDataBuffer);

    }

Now, this does not work. The reason is it takes too long. I am sampling audio from a sound card at 192,000 samples a second. The event above fires each time there is a 4096 buffer available. Thus every 20 milliseconds a new buffer arrives. This buffer is then sent to a fast fourier transform routine that produces a graph of signal strength vs frequency. The imaginary data is actually the same as the real data but it is phase shifted by 90 degrees. This is how it is in a perfect world. However the phase is never 90 degrees due to imbalances in the signal, different electronics paths, different code and so on. Thus one needs a correction factor and that is what the equations do. Now if I replace the

data[i] = data[i];
data[i + 1] = data[i] * Math.Cos(phaseCorrection) + data[i+1] * Math.Sin(phaseCorrection); 

with

data[i] = data[i];
data[i+1] = data[i+1];

it works ok.
So I'm going to need pointers. But will this be substantially faster or will the sin and cosine functions just take too long? Cn I send the data directly to the processor (now thats getting real unsafe!).

You can't access unmanaged data directly in safe context, so you need to do a copy of data to managed container:

using System.Runtime.InteropServices;
// ...

var data = new double[4096];
Marshal.Copy(pointer, data, 0, 4096);
// process data here

And as far as I see it, unsafe context is not required for this part of code.

As for the writing data back it's actually quite the same, but note the parameters order:

// finished processing, copying data back
Marshal.Copy(data, 0, pointer, 4096);

As for your last notes on perfprmance. As I already mentioned, move var data = new double[8192]; to a data class member and initialize it in class constructor, so it's not allocated each time (and allocation is pretty slow). You can do Math.Cos(phaseCorrection) outside of cycle since phaseCorrection is unchanged, same is for Math.Sin(phaseCorrection) of course. Something like this:

Marshal.Copy(pointer, this.data, 0, 8192);

var cosCache = Math.Cos(phaseCorrection);
var sinCache = Math.Sin(phaseCorrection);
for (int i = 0; i < 8192; i+=2)
{
    data[i] = data[i];
    data[i + 1] = data[i] * cosCache + data[i+1] * sinCache; 
}

Marshal.Copy(this.data, 0, pointer2, 8192);

All this should speed up things tremendously.

If you have an IntPtr , you can get a pointer from it. Using your example:

    unsafe
    {
     IntPtr pointer =  Args.InBuffer.Read(); 

     // create a pointer to a double
     double* pd = (double*)pointer.ToPointer();

     // Now you can access *pd
     double firstDouble = *pd;
     pd++;  // moves to the next double in the memory block
     double secondDouble = *pd;

     // etc.
     }

Now, if you want to put data back into the array, it's much the same:

double* pd = (double*)pointer.ToPointer();
*pd = 3.14;
++pd;
*pd = 42.424242;

You need to read and understand Unsafe Code and Pointers . Be very careful with this; you're programming without a safety net here.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM