用C ++ ASIO库在C#中录制音频流

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

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

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

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

我也想建立一个波形图像。 我已经在C ++中完成了这些工作,但不知怎的,我不喜欢这个想法编写过多的消息传递,传输对象等。

所以把东西放在一起:

  • 我有C ++非托管回调,做一些东西,从内部我想调用C#方法一旦它处理了数据,C原型将是:

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

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

  • 什么是最好的类写连续流(如mem.put(float [] [] data,int size)),然后以数组或其他简单的方式读取它(例如保存一个wav文件或制作波形位图)

  • 如果我在C#中执行操作,会有显着的性能损失吗? (托管c ++包装调用c#函数等...可能还有一些DSP的东西),或者我只是偏执狂? :)

  • 干杯,

    pablox

    我的解决方案

    好吧,我解决了这个问题:

    在我的C ++头文件中我有传输结构:

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

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

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

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

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

    那个代表也有C的原型

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

    内部C ++类使用的是:

    void                initializeSoundSystem(HWND HWnd, GETAUDIODATA audio);
    

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

    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);
    }
    

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

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

                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);
    

    而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);
                }));
            }
        }
    }
    

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


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

    delegate void ASIOCallback(IntPtr signal, int n);
    

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

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

    问题3:您需要对性能进行两个考虑,即原始数据处理和复制数据。 一般来说,C#中的数值操作并不比C / C ++慢很多。 网上有很多很好的资源来比较这种类型的表现。 至于复制数据,我认为这可能会给您带来更多问题。 这两者都是由于复制数据所花费的时间以及应用程序将使用的额外内存。 您需要考虑是否需要在托管和非托管内存中的数据副本,并且我猜这将取决于您希望在C#和/或C ++中访问它的次数。

    链接地址: http://www.djcxy.com/p/33853.html

    上一篇: Recording an audio stream in C# from C++ ASIO library

    下一篇: Obtaining the wav file from ServletInputStream