Martin Tarrou
Martin Tarrou

Reputation: 67

Using a Pointer as a Buffer in C# for an External Function

I'm importing an external C++ function from a dylib into Unity C#. The function in question looks like this:

[DllImport ("librtcmix_embedded")]
unsafe private static extern int RTcmix_runAudio(void* k, void *outAudioBuffer, int nframes);

It takes in a pointer and writes the audio information to that pointer in a fixed size buffer. I have a C# function from Unity which is supposed to take this information into a buffer:

 void OnAudioFilterRead(float[] data, int channels)

Ideally within the body of OnAudioFilterRead, I will be able to declare a pointer of fixed size int* (2048, the number of samples Unity's audio uses) and feed that into the outAudioBuffer parameter of RTcmix_runaudio, then copy the information from that pointer into the float array of data.

    void OnAudioFilterRead(float[] data, int channels)
{
    int *buffer = new int*(2048); //this line is not proper c#, how do I do this?
    RTcmix_runAudio (null, buffer, 2048);
    for(int i = 0; i<2048; i++){
        data[i] = (float) buffer[i];
    }
}

However, I have no real idea how to get a properly working pointer with a size of 2048 in C#. Any help with this? All my attempts to input arrays or use fixed structs have crashed the program.

Upvotes: 1

Views: 1903

Answers (1)

pathfinder
pathfinder

Reputation: 134

By using Marshal.AllocHGlobal you can allocate an unmanaged buffer of 8192 bytes (2048 x 32 bit samples), pointed to by IntPtr and pass that to the DLL. You then use Marshal.Copy to copy the unmanaged buffer content into a managed int[]. I then used Linq to 'cast' the buffer into a float[]. Free the unmanaged buffer with FreeHGlobal.

I'm assuming that the buffer consists of 4 byte integers. If it actually contains float then change the type of destination to float[], and C# will call the correct Marshal.Copy.

using System;
using System.Linq;
using System.Runtime.InteropServices;

public static class DLL
{
    [DllImport("librtcmix_embedded")]
    public static extern int RTcmix_runAudio(IntPtr k, IntPtr outAudioBuffer, int nframes);
}

public class MyTest
{
    void OnAudioFilterRead(out float[] data, int channels)
        {
            int[] destination = new int[channels];
            IntPtr buffer = Marshal.AllocHGlobal(4*channels);

            DLL.RTcmix_runAudio((IntPtr)0, buffer, channels);            

            Marshal.Copy(buffer, destination, 0, channels);

            Marshal.FreeHGlobal(buffer);

            data = destination.Select(item => (float)item).ToArray();

        }
}

Upvotes: 1

Related Questions