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#.
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.
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.
Is there any fast resizable container in C#, where i could just put the data inside and later access it like an array?
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.
So to put things together:
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:
void process(float **signal, int n); (usually [2][n] - for stereo)
What would be C# equivalent and how do i call it from that C++ callback ?
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)
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 ? :)
Cheers,
pablox
My solution
Ok i solved it that way:
In my C++ header file i got transfer structure:
public ref struct CVAudio {
public:
float *left;
float *right;
int length;
};
Then in managed C++ class i have declared:
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
typedef void (CALLBACK *GETAUDIODATA)(CVAudio ^a);
Which is used in the internal C++ class as:
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.
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:
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. :)