从C ++ ASIO库中以C#录制音频流

时间:2022-09-08 23:22:06

I need to find the best way to record an audio stream. I have already built the low level code in C++ and interfaced parts of it to C#.

我需要找到录制音频流的最佳方法。我已经在C ++中构建了低级代码,并将其部分接口到C#。

So i have a C++ callback that gives me an array of array of floats - the audio signal. At the moment, my C++ lib is recording the data straight to the file in the wav format and it just notify my C# app when it ends recording.

所以我有一个C ++回调,给我一个浮点数组 - 音频信号。目前,我的C ++ lib正在以wav格式将数据直接记录到文件中,它只是在结束录制时通知我的C#应用​​程序。

But, I would like to have more interactivity on the UI side, like 'infinite' progress bar, amount of the data recorded, cancel button etc, and since it's gonna be a minute at worst, maybe it's better to keep it in memory. I know very little about .NET and C# memory management, so i don't know how to make it efficiently.

但是,我希望在UI方面有更多的交互性,比如“无限”进度条,记录的数据量,取消按钮等,并且因为它会在最坏的情况下是一分钟,也许最好将它保存在内存中。我对.NET和C#内存管理知之甚少,所以我不知道如何有效地实现它。

Is there any fast resizable container in C#, where i could just put the data inside and later access it like an array?

在C#中是否有任何快速可调整大小的容器,我可以将数据放入其中并稍后像数组一样访问它?

I also would like to build a waveform image off it. I already have those things done in C++, but somehow i don't like the idea to write too much messaging, transfer objects etc.

我还想建立一个波形图像。我已经用C ++完成了这些事情,但不知怎的,我不喜欢写太多消息,转移对象等的想法。

So to put things together:

所以把事情放在一起:

  1. I have C++ unmanaged callback that does some stuff and from within i'd like to call C# method once it has processed the data, the C prototype would be:

    我有C ++非托管回调,它做了一些事情,并且从内部我想在处理数据后调用C#方法,C原型将是:

    void process(float **signal, int n); (usually [2][n] - for stereo)

    void process(float ** signal,int n); (通常[2] [n] - 用于立体声)

    What would be C# equivalent and how do i call it from that C++ callback ?

    什么是C#等价物,我如何从C ++回调中调用它?

  2. What is the best class to write continuous stream to (like mem.put(float[][] data, int size) ) and then read it as an array or with other easy way (to save a wav file from it for example or make a waveform bitmap)

    写连续流的最佳类是什么(如mem.put(float [] [] data,int size))然后将其作为数组或其他简单方法读取(例如从中保存wav文件或者制作波形位图)

  3. Is there going to be a significant performance loss if i do it in C#? (managed c++ wrapper calling c# function etc... and probably some DSP stuff) or i am just paranoid ? :)

    如果我在C#中这样做会不会有重大的性能损失? (托管c ++包装器调用c#函数等...可能还有一些DSP的东西)或者我只是偏执狂? :)

Cheers,

pablox

My solution

Ok i solved it that way:

好的,我这样解决了:

In my C++ header file i got transfer structure:

在我的C ++头文件中,我得到了传输结构:

public ref struct CVAudio {
   public:
    float *left;
    float *right;
    int length;
   };

Then in managed C++ class i have declared:

然后在托管C ++类中我声明了:

delegate void       GetAudioData([In, Out] CVAudio^ audio);

Then i can use it as an argument to the method that initialize audio:

然后我可以将它用作初始化音频的方法的参数:

void                initializeSoundSystem(void *HWnd, GetAudioData ^audio);

That delegate has also a C prototype of

那个代表也有一个C原型

typedef void (CALLBACK *GETAUDIODATA)(CVAudio ^a);

Which is used in the internal C++ class as:

在内部C ++类中使用的是:

void                initializeSoundSystem(HWND HWnd, GETAUDIODATA audio);

Then body of the first method is:

然后第一种方法的主体是:

void VDAudio::initializeSoundSystem(void *HWnd, GetAudioData ^audio) 
{
    HWND h = (HWND) HWnd;

    acb = audio;
    pin_ptr<GetAudioData ^> tmp = &audio;

    IntPtr ip = Marshal::GetFunctionPointerForDelegate(audio);
    GETAUDIODATA cb = static_cast<GETAUDIODATA>(ip.ToPointer());

    audioObserver->initializeSoundSystem(h, cb);
}

Body of audioObserver just stores that callback in the object and do some audio related things.

audioObserver的主体只是将该回调存储在对象中并做一些与音频相关的事情。

Callback is then called in the processing method like that:

然后在处理方法中调用回调,如下所示:

            VDAudio^ a = gcnew VDAudio();
            a->left = VHOST->Master->getSample()[0]; //returns left channel float*
            a->right = VHOST->Master->getSample()[1];
            a->length = length;
            (*callback)(a);

And the body of C# delegate:

和C#委托的主体:

public void GetSamples(CVAudio audio)
{
    unsafe
    {

        float* l = (float*)audio.left;
        float* r = (float*)audio.right;

        if (l != null)
        {

            SamplePack sample = new SamplePack();

            sample.left = new float[audio.length];
            sample.right = new float[audio.length];

            IntPtr lptr = new IntPtr((void*)l);
            IntPtr rptr = new IntPtr((void*)r);

            Marshal.Copy(lptr, sample.left, 0, audio.length);
            Marshal.Copy(rptr, sample.right, 0, audio.length);

            this.Dispatcher.Invoke(new Action(delegate()
            {
                GetSamples(sample);
            }));
        }
    }
}

So probably that's not a best code around - i have no idea. Only can say it works, doesn't seem to leak etc. :)

所以可能这不是最好的代码 - 我不知道。只能说它有效,似乎没有泄漏等:)

1 个解决方案

#1


2  

Question 1: You want to pass a C# delegate matching the unmanaged C signature into your C++. You can then call back on this as if it was a C function pointer. For example,

问题1:您希望将与非托管C签名匹配的C#委托传递到C ++中。然后,您可以回调它,就好像它是一个C函数指针。例如,

delegate void ASIOCallback(IntPtr signal, int n);

You would then need to either manually marshal the signal memory into a managed buffer, or copy it into a unmanaged buffer. Either would be done using methods on the Marshal class. This leads into question 2.

然后,您需要手动将信号内存编组到托管缓冲区中,或将其复制到非托管缓冲区中。要么使用Marshal类上的方法来完成。这引出了问题2。

Question 2: You have a couple of choices, primarily determined by whether you want to store the data in managed or unmanaged memory. Storing it unmanaged memory may make it easy to interact with you unmanaged code and could theoretically be more efficient by reducing the number of copies of the data. However, storing it in managed memory (via an array, generic collection, MemoryStream, etc) would make it much easier to access from managed code.

问题2:您有几个选择,主要取决于您是否要将数据存储在托管或非托管内存中。存储非托管内存可以很容易地与您的非托管代码进行交互,理论上可以通过减少数据副本的数量来提高效率。但是,将其存储在托管内存中(通过数组,泛型集合,MemoryStream等)可以更容易地从托管代码进行访问。

Question 3: The two considerations you need to make regarding performance are raw number crunching and duplicating you data. In general, numerical operations in C# are not much slower than C/C++. There are multiple good sources on the web for comparing this type of performance. As far as copying data, I think this may cause more of an issue for you. Both due to the time spent copying the data and the extra memory you application will use. You need to consider whether you want a copy of your data in both managed and unmanaged memory, and I am guessing this will be driven by how much you want to access it in C# and/or C++.

问题3:关于性能需要考虑的两个因素是原始数字处理和复制数据。通常,C#中的数值运算并不比C / C ++慢得多。网上有很多好的资源来比较这种类型的表现。至于复制数据,我认为这可能会给您带来更多问题。由于复制数据所花费的时间和应用程序将使用的额外内存。您需要考虑是否需要托管和非托管内存中的数据副本,我猜这将取决于您希望在C#和/或C ++中访问它的程度。

#1


2  

Question 1: You want to pass a C# delegate matching the unmanaged C signature into your C++. You can then call back on this as if it was a C function pointer. For example,

问题1:您希望将与非托管C签名匹配的C#委托传递到C ++中。然后,您可以回调它,就好像它是一个C函数指针。例如,

delegate void ASIOCallback(IntPtr signal, int n);

You would then need to either manually marshal the signal memory into a managed buffer, or copy it into a unmanaged buffer. Either would be done using methods on the Marshal class. This leads into question 2.

然后,您需要手动将信号内存编组到托管缓冲区中,或将其复制到非托管缓冲区中。要么使用Marshal类上的方法来完成。这引出了问题2。

Question 2: You have a couple of choices, primarily determined by whether you want to store the data in managed or unmanaged memory. Storing it unmanaged memory may make it easy to interact with you unmanaged code and could theoretically be more efficient by reducing the number of copies of the data. However, storing it in managed memory (via an array, generic collection, MemoryStream, etc) would make it much easier to access from managed code.

问题2:您有几个选择,主要取决于您是否要将数据存储在托管或非托管内存中。存储非托管内存可以很容易地与您的非托管代码进行交互,理论上可以通过减少数据副本的数量来提高效率。但是,将其存储在托管内存中(通过数组,泛型集合,MemoryStream等)可以更容易地从托管代码进行访问。

Question 3: The two considerations you need to make regarding performance are raw number crunching and duplicating you data. In general, numerical operations in C# are not much slower than C/C++. There are multiple good sources on the web for comparing this type of performance. As far as copying data, I think this may cause more of an issue for you. Both due to the time spent copying the data and the extra memory you application will use. You need to consider whether you want a copy of your data in both managed and unmanaged memory, and I am guessing this will be driven by how much you want to access it in C# and/or C++.

问题3:关于性能需要考虑的两个因素是原始数字处理和复制数据。通常,C#中的数值运算并不比C / C ++慢得多。网上有很多好的资源来比较这种类型的表现。至于复制数据,我认为这可能会给您带来更多问题。由于复制数据所花费的时间和应用程序将使用的额外内存。您需要考虑是否需要托管和非托管内存中的数据副本,我猜这将取决于您希望在C#和/或C ++中访问它的程度。