2

I'm trying to create a binding to the Linea Pro (it's the barcode scanner they use in the Apple Stores, Lowes) SDK. I'm using David Sandor's bindings as a reference, but the SDK has been updated a few times since January of 2011.

I have most everything working, except for the playSound call, which is used to, well, play a sound on the Linea Pro device.

The .h file from the SDK has the call as follows:

-(BOOL)playSound:(int)volume beepData:(int *)data length:(int)length error:(NSError **)error;

I've tried using int[], NSArray, and an IntPtr to the int[], but nothing seems to work.

The last unsuccessful iteration of my binding looks like:

[Export ("playSound:beepData:length:")]
void PlaySound (int volume, NSArray data, int length);

Now, this doesn't work at all. Also note that I have no idea what to do with the error:(NSError **)error part, either.

I am lacking some serious familiarity with C, so any help would be extremely appreciated.

Wayne Hartman
  • 18,369
  • 7
  • 84
  • 116
jeffrapp
  • 58
  • 1
  • 6

2 Answers2

1

You cannot use NSArray unless the Objective-C code uses NSArray, i.e. the generator allows us to map some ObjC constructs to .NET types (e.g. NSString to string) but it won't allow you to redefine ObjC types.

-(BOOL)playSound:(int)volume beepData:(int *)data length:(int)length error:(NSError **)error;

should be like:

[Export ("playSound:beepData:length:error:")]
bool PlaySound (int volume, IntPtr data, int length, out NSError error);

You'll need to marshal your data into an IntPtr.

IntPtr data = Marshal.AllocHGlobal (length);
Marshal.WriteInt32 (data1, 0);

and free it afterward.

Marshal.FreeHGlobal (data);

That's best done using a public helper method which calls your internal binding. You can make the PlaySound method internal by adding a [Internal] attribute to it's definition. So it becomes:

[Export ("playSound:beepData:length:error:")][Internal]
bool PlaySound (int volume, IntPtr data, int length, out NSError error);

and you include the following code with your bindings (e.g. API.cs):

bool PlaySound (int volume, int[] data)
{
    // I assume length is byte-based (check the docs)
    int length = data.Length * 4; 
    IntPtr p = Marshal.AllocHGlobal (length);
    int j = 0;
    for (int i=0; i < length; i+=4)
        Marshal.WriteInt32 (p [j++], i);
    NSError error;
    bool result = PlaySound (volume, p, length, out error);
    // free memory before throwing the exception (if any)
    Marshal.FreeHGlobal (data);
    if (error != null)
       throw new Exception (error.LocalizedDescription);
    return result;
}

note: totally untried :-) I do not have the hardware, SDK or documentation. YMMV but that should be close.

poupou
  • 43,413
  • 6
  • 77
  • 174
  • You're a gentleman and a scholar. I'll give this a try tomorrow morning. Thanks! – jeffrapp Sep 16 '12 at 16:29
  • It's a no-go. Mono balks at the Marshal.WriteInt32 (p [j++], i), so I changed it to `Marshal.WriteInt32 (p, i, data[j++])`, still nothing. Also tried to get the IntPtr with GCHandle.Alloc, nothing. I tried in xcode, `int sound[]={2730,150,0,30,2730,2730}; int size = sizeof(sound); [linea playSound:100 beepData:sound length:size error:nil];`, and this works. When the debugger hits the actual PlaySound line, the application output shows `-[Linea playSound:beepData:length:error]: unrecognized selector sent to instance 0xa2e6b40`, but no real error is thrown. – jeffrapp Sep 17 '12 at 14:48
  • I do not know what's your original data format. Look at other `Marshal` methods (like `Copy`) to copy your data. You can also try to bind this as a `(int*)` and used `unsafe fixed` in your helper method. But without hardware and context all we can do is guess ;-) – poupou Sep 17 '12 at 14:54
  • 1
    @jeffrapp the export should be: [Export ("playSound:beepData:length:error:")]. That will prevent the unrecognized selector issue. Note the ':' at the end, that is required. – TrustMe Sep 18 '12 at 22:35
1

I was having the same trouble. However the help provided by poupou above was enough to get me on the right track. My linea pro device now gives the double-beep when I ask it to, so I thought I should follow-up with working tested code. Forgive any style messups, this is my first post to stackoverflow...

Here is the export definition I used. It's the same as the one suggested above, just wanted to verify that it worked.

    [Export ("playSound:beepData:length:error:")]
    bool PlaySound (int volume, IntPtr data, int length, out NSError error);

From there, I just had to learn enough c# to get the marshalling bit straight. (i'm new to c# too) as you can see, it's just a patched up bit from what was posted above. many thanks for pointing me in the right direction!

    public void Beep()
    {
        int[] sound = {2730, 150, 0, 30, 2730, 150};
        PlaySound(100, sound);
    }

    public bool PlaySound(int volume, int[] data)
    {

        // length is byte-based
        int length = data.Length*4;
        IntPtr p = Marshal.AllocHGlobal(length);

        for (int i = 0; i < data.Length; i ++)
        {
            Marshal.WriteInt32(p, i*4, data[i]);
        }

        NSError error;
        bool result = dtDevice.PlaySound(volume, p, length, out error);

        // free memory before throwing the exception (if any)
        Marshal.FreeHGlobal(p);

        if (error != null)
            throw new Exception(error.LocalizedDescription);

        return result;

    }