|
Have you allocated and pinned the byte[] arrays before calling the function?
|
|
|
|
|
No, I have not. From the C++ example, they show setting the pbuf size, but with using IntPtr I was not sure how to accomplish the same thing.
|
|
|
|
|
Create a byte[] array with the same size you need in C++ for UCHAR. Use GCHandle class to pin this array, so the GC will not reallocate it in case of a garbage collection, and set its address to the pbuf field. Have a look at the documentation of GCHandle class.
|
|
|
|
|
I have modified the code as per my understanding of GCHandle. I am still getting memory errors. I believe it has something to do with the IntPtr of the pbuf in the MY_IMAGE struct.
Here is my code so far:
[StructLayout(LayoutKind.Sequential)]
public class MY_IMAGE
{
public int width;
public int height;
public int info;
public IntPtr pbuf;
}
[DllImport("scandll.dll")]
private extern static int WScanSelectBuf(IntPtr image_down, IntPtr image_up, int select);
public static int ScanSelectBuf(MY_IMAGE image_down, MY_IMAGE image_up, int select)
{
GCHandle image_downHandle = GCHandle.Alloc(image_down, GCHandleType.Pinned);
GCHandle image_upHandle = GCHandle.Alloc(image_up, GCHandleType.Pinned);
int ret = WScanSelectBuf(image_downHandle.AddrOfPinnedObject(),
image_upHandle.AddrOfPinnedObject(), select);
image_downHandle.Free();
image_upHandle.Free();
return ret;
}
MY_IMAGE img1 = new MY_IMAGE();
MY_IMAGE img2 = new MY_IMAGE();
int nWidth = 864;
int nHeight = 1000;
img1.width = nWidth;
img1.height = nHeight;
img1.info = 8;
img2.width = nWidth;
img2.height = nHeight;
img2.info = 8;
ret = ScanSelectBuf(img1, img2, 0);
Anything I am missing ???
|
|
|
|
|
Yes, you are pinning the MY_IMAGE objects, but that is not what you have to pin. When you call a function using P/Invoke, the CLR automatically pinnes in the managed heap the references you pass, so there is no need to explicitly pin them as you have done. However, the pbuf field of each MY_IMAGE class is an IntPtr, what means that it only contains the memory address of the buffer allocated to receive de image pixels. As it is only an address, the CLR will not pin these buffers automatically, and that is why the programmer has to pin them manually before using the P/Invoke call. In this case you have not even allocated the buffers, so the values of the pbuf fields of the MY_IMAGE objects you are passing to the unmanaged function are IntPtr.Zero, I mean, the default value for IntPtr.
This is what you have to do:
Before you call the WScanSelectBuf you have to create the two MY_IMAGE objects and allocate the byte[] arrays where that function will place the image pixel:
MY_IMAGE img1 = new MY_IMAGE();
MY_IMAGE img2 = new MY_IMAGE();
byte[] buffer1 = new byte[2*img1.width*img1.height];
byte[] buffer2 = new byte{2*img2.width*img2.height];
GCHandle bufferHandle1 = GCHandle.Alloc(buffer1, GCHandleType.Pinned);
GCHandle bufferHandle2 = GCHandle.Alloc(buffer2, GCHandleType.Pinned);
img1.pbuf = bufferHandle1.AddrOfPinnedObject();
img2.pbuf = bufferHandle2.AddrOfPinnedObject();
int ret = WScanSelectBuf(img1, img2, select);
bufferHandle1.Free();
bufferHandle2.Free();
Tell us if it works.
|
|
|
|
|
I modified the code as per above. The img1.pbuf and img2.pbuf are now showing an address. However, the native API funtion of WScanSelectBuf(img1, img2, select) is still crashing. I have tried passing in:
WScanSelectBuf(img1, img2, select) (MY_IMAGE)
WScanSelectBuf(ref img1, ref img2, select) (ref of MY_IMAGE)
WScanSelectBuf(img1, img2, select) (IntPtr)
and changed the code in the DLLImport and wrapper method without any success. Is there anything I am missing with Marshaling, etc?
I am coming to the conclusion that it could be an issue with the 3rd party's API libary and is doing something odd with the function. However, it does work in their C++ sample.
I have contacted the vendor and they are not familar with .NET. However, they are reviewing this thread and for any suggestions.
|
|
|
|
|
Ok, it's pretty weird... I am not sure what is going on here. The correct way to import MY_IMAGE type and the function is as I told you before, there is no need of marshaling for any field. You might try a little change, using Marshal.UnsafeAddrOfPinnedArrayElement instead of AddrOfPinnedObject method of GCHandle class, I mean, you first pin the buffers with GCHandle and then set the address in pbuf fields this way:
img1.pbuf = Marshal.UnsafeAddrOfPinnedArrayElement(buffer1, 0);
img2.pbuf = Marshal.UnsafeAddrOfPinnedArrayElement(buffer2, 0);
If it still does not work, you might try allocating the buffers directly in the global heap, with Marshal.AllocHGlobal (remember to free this memory when you don't need it any more), instead of creating them in the managed heap as we did before when instantiated the buffers with new byte[size]. If this works you should then copy the buffers allocated in the global heap to the managed heap with the method Marshal.Copy.
|
|
|
|
|
Ok, below are the changes I tried. However, I am receiving an error of: "Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
[DllImport("scandll.dll")]
private extern static int WScanSelectBuf(MY_IMAGE image_down, MY_IMAGE image_up, int select);
public static int ScanSelectBuf(MY_IMAGE image_down, MY_IMAGE image_up, int select)
{
return WScanSelectBuf(image_down, image_up, select);
}
int nWidth = 100;
int nHeight = 200;
img1.width = nWidth;
img1.height = nHeight;
img1.info = 8;
img2.width = nWidth;
img2.height = nHeight;
img2.info = 8;
byte[] buffer1 = new byte[2 * img1.width * img1.height];
byte[] buffer2 = new byte[2 * img2.width * img2.height];
GCHandle bufferHandle1 = GCHandle.Alloc(buffer1, GCHandleType.Pinned);
GCHandle bufferHandle2 = GCHandle.Alloc(buffer2, GCHandleType.Pinned);
img1.pbuf = Marshal.UnsafeAddrOfPinnedArrayElement(buffer1, 0);
img2.pbuf = Marshal.UnsafeAddrOfPinnedArrayElement(buffer2, 0);
ret = ScanSelectBuf(img1, img2, 0);
bufferHandle1.Free();
bufferHandle2.Free();
|
|
|
|
|
Erik - Thanks for your help.
I have it working now. The reason it was failing after the last step was due to other settings that had to be set on the scanner. The vendor makes a single side and double side scanner. The sample in the documetation does not match the process. I dug around in the C++ sample and found other settings that I was able to specify before scanning the card.
|
|
|
|
|
One of the functions was not in the documentation and the sample I was using. I found the function when reviewing the header file and the C++ test app code.
|
|
|
|
|
Erik,
I wanted to thank you again for the assistance.
I was able to confirm that the code require the buffer and pinning/marshaling of the memory and it will not work correctly without the suggestions you provided.
I also wanted to let you know what compounded the testing issue with the error was due to incomplete documentation. After getting the process to work, I was able to change certain settings and was able to cause the process to fail. Basically, the image class has to define the height and width to the MAXIMUM allowed image that the scanner can accept. Then the buffer size is set to the max height * max width * single/double scanner mode. Once the scanner returns the image, it adjusts the height and buffer values to be what are read in from the scanner. If the image sizes are specified incorrectly/too small, the API will cause a memory error. For example, the image of the scanned area may be 4" but the scanner's read head is capable of scanning a wider area. So the image width has to be pre-set the maximum width of the read head and not the width of the item that is being scanned. BTW, these maximum values are not specified in the documentation or samples. I actually had to dig thru a C++ sample application and the header files to find these settings.
Thanks again.
|
|
|
|
|
You're welcome. I'm glad to know you finally solved it.
|
|
|
|
|
It is very fortunate the caller is responsible for the buffer, so you can have it all managed. You need to allocate a byte array of sufficient capacity, and pass its pointer (an IntPtr in .NET); you won't need any Marshal attributes, not even any unsafe stuff. Look for the GCHandle class, and read how I pass arrays in this article[^]. Don't forget to free the GCHandle once you don't need it any more (which could be well after the return of the first native function, if it happens to keep the pointer around).
Luc Pattyn [Forum Guidelines] [My Articles] Nil Volentibus Arduum
Please use <PRE> tags for code snippets, they preserve indentation, improve readability, and make me actually look at the code.
|
|
|
|
|
This was an example of another library that I was using as reference. It was confusing so I removed it since it is not the code I am having issues with.
modified on Thursday, January 13, 2011 8:48 PM
|
|
|
|
|
Not sure what your question is here.
I would comment on the code like this:
1. it does not seem complete. Where is the buffer allocated?
2. I guess, but am not sure, the Marshal attributes used are sufficient to get the buffer pinned for as long as the native method lasts, but no longer.
3. Constructing an image from native data is fine, however it requires the underlying data to remain available as long as the image object lives (see MSDN doc), and that is not very easy to implement correctly. It sure does not show in your code.
Luc Pattyn [Forum Guidelines] [My Articles] Nil Volentibus Arduum
Please use <PRE> tags for code snippets, they preserve indentation, improve readability, and make me actually look at the code.
|
|
|
|
|
Ok, I have looked a little more at your article. The Marshalling and pinning of the memory is something that I dont work with much. However, it does make sense with the type of memory error that I am receiving.
So, from the original code I posted, how would you maybe suggest I change the stuct, DllImport, and calling methods to be in line with the Marshalling, pinning, and GCHandle? Would the parameter types change?
|
|
|
|
|
You now have published two code snippets, I don't know which one you're talking about right now.
However this is what I would to:
1.
on the former snippet, take my advice, i.e. apply the third method of passing an array (with GCHandle, without Marshal, without unsafe). I expect that to work perfectly right away. And it does not require any change on the native side.
2.
on the latter snippet, if the former now works well and you see sufficient reason to modify the latter, modify it to better correspond to the former.
Luc Pattyn [Forum Guidelines] [My Articles] Nil Volentibus Arduum
Please use <PRE> tags for code snippets, they preserve indentation, improve readability, and make me actually look at the code.
|
|
|
|
|
Sorry for the latter code. Please ignore it. Maybe I can delete it.
|
|
|
|
|
econner wrote: Any suggestions would be appreciated.
Don't embed it in in your C#/.Net code.
Instead wrap it in a C++ executable with a communication (socket/file/stdio) API and run it from C# via Process and write C# code to talk to the communication API.
The advantage is that it can't take down your C# app and you can test the parts independently.
Note the first part as that can happen even if your code is perfect if there is a bug in the library itself.
|
|
|
|
|
Hello everyone,
i wan't to create something like a active noise cancellation system . So i want to record audio with a mic in c# and modify the signal, that i get the "negative" or opposite signal and play that negative signal of the ingoing signal.
I've never recorded any audio and don't understand, what i get in such a recorded stream. Do i get simple integer values, which i only need to opposite (value * -1) and write them to another stream and put them through or what do i need to do?
|
|
|
|
|
Hmm. Good luck!
Firstly, if you get and audio stream there are many different encodings it could come in. Probably the most common would be 44.1KHz 16 bit. So you'd be dealing with buffers of shorts.
Obviously, if you negate this signal and add this to the original, everything cancels out and you end up with silence, so I suspect what you want to do is use two input signals, a reference one, and a background one.
Now, because the mics will be in different positions, you'll likely get a phase shift and this will vary depending on the frequency composition of what you're recording. You could actually end up boosting a signal rather than cancelling it if you phase is shifted 180 degrees.
I would imagine an effective way to do this is firstly to translate the signal from the time domain to the frequency domain, then compare and adjust the amplitude of the frequency bands, and convert it back again. This involves heavy duty mathematics. Lower frequencies are easier to cancel than higher ones as you may have observed if you have some noise cancelling headphones.
Scary stuff, and with the best of respect unless you're well versed in the field of digital signal processing I'd consider doing something else.
Regards,
Rob Philpott.
|
|
|
|
|
If all you want to do is change the phase of your audio signal, you don't need a computer or an app, all it takes is swapping the wires to the speaker!
However, noise, by its very definition, is a random signal; meaning whatever it is you capture with a MIC is a thing of the past and will sound like, but be far from identical to, the noise you will encounter later. Cancellation by adding something works fine on predictable sounds, such as the (50Hz or so) rumble of an engine; noise cancellation normally is done through filtering, not by addition.
Luc Pattyn [Forum Guidelines] [My Articles] Nil Volentibus Arduum
Please use <PRE> tags for code snippets, they preserve indentation, improve readability, and make me actually look at the code.
|
|
|
|
|
Sincerely, I don't know. You might begin by searching for "computational harmonic analysis" and "Fourier transform"... and good luck.
|
|
|
|
|
A simple method (for sine wave signals or predictable noise) is to simply invert the signal and play back at point of origin.
This is used in engine noise reduction systems and in top-end industrial machinery.
------------------------------------
I will never again mention that I was the poster of the One Millionth Lounge Post, nor that it was complete drivel. Dalek Dave
CCC League Table Link
CCC Link[ ^]
|
|
|
|
|
Exactly that is what i want to do!
I know, that callibration of that isn't simple and so on, but i want to try it.
If it would be easy, it would by uninteresting.
So how can i record audio from a microphone and simply invert the signal and put it out over my speakers?
|
|
|
|
|