pab*_*lox 5 c# c++ audio wpf asio
我需要找到录制音频流的最佳方式.我已经在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;
};
Run Code Online (Sandbox Code Playgroud)
然后在托管C++类中我声明了:
delegate void GetAudioData([In, Out] CVAudio^ audio);
Run Code Online (Sandbox Code Playgroud)
然后我可以用它作为初始化音频的方法的参数:
void initializeSoundSystem(void *HWnd, GetAudioData ^audio);
Run Code Online (Sandbox Code Playgroud)
那个代表也有一个C原型
typedef void (CALLBACK *GETAUDIODATA)(CVAudio ^a);
Run Code Online (Sandbox Code Playgroud)
在内部C++类中使用的是:
void initializeSoundSystem(HWND HWnd, GETAUDIODATA audio);
Run Code Online (Sandbox Code Playgroud)
然后第一种方法的主体是:
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);
}
Run Code Online (Sandbox Code Playgroud)
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);
Run Code Online (Sandbox Code Playgroud)
和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);
}));
}
}
}
Run Code Online (Sandbox Code Playgroud)
所以可能这不是最好的代码 - 我不知道.只能说它有效,似乎没有泄漏等:)
问题 1:您想要将与非托管 C 签名匹配的 C# 委托传递到您的 C++ 中。然后您可以回调它,就好像它是一个 C 函数指针一样。例如,
delegate void ASIOCallback(IntPtr signal, int n);
Run Code Online (Sandbox Code Playgroud)
然后,您需要手动将信号内存编组到托管缓冲区中,或者将其复制到非托管缓冲区中。任一方法都可以使用Marshal 类上的方法来完成。这就引出了问题2。
问题 2:您有几个选择,主要取决于您是否要将数据存储在托管内存或非托管内存中。将其存储在非托管内存中可以使与非托管代码的交互变得容易,并且理论上可以通过减少数据副本的数量来提高效率。但是,将其存储在托管内存中(通过数组、通用集合、MemoryStream等)将使从托管代码访问变得更加容易。
问题 3:关于性能,您需要考虑的两个因素是原始数字处理和数据复制。一般来说,C#中的数值运算并不比C/C++慢多少。网络上有多个很好的资源可用于比较此类性能。至于复制数据,我认为这可能会给您带来更多问题。这是因为复制数据所花费的时间以及应用程序将使用的额外内存。您需要考虑是否需要在托管和非托管内存中保存数据副本,我猜测这将取决于您希望在 C# 和/或 C++ 中访问数据的程度。