Foreign Handlers and Custom Types

LiveCode Builder is a language for extending LiveCode's capabilities, creating new object types as Widgets, and libraries that access lower-level APIs in OSes, applications, and DLLs.

Moderators: LCMark, LCfraser

n.allan
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 153
Joined: Mon Mar 12, 2007 12:06 pm

Re: Foreign Handlers and Custom Types

Post by n.allan » Thu Oct 01, 2015 3:34 pm

No sweat, we are all in the same boat with LCB!

You shouldn't really need to use dlopen() or LoadLibrary(). That is the beauty of LCB. The "binds to" directive does exactly that.

The only time I looked at the C code of a library was to check which type of variables are being passed and returned to each function.

Please find attached some code that I used to bind to libfluidsynth.

FYI I can bind to the dll if it is in the same folder as the lcb script too....AND you can bind to it in a subfolder too if you specify it in the "binds to" directive. I will explain after this.

LCB Code

Code: Select all

library community.livecode.everyone.fluidsynth

metadata title is "Fluid Synth Library"
metadata author is "Author"
metadata version is "1.0.0"

use com.livecode.foreign

foreign handler new_fluid_settings ( ) returns CUint binds to "libfluidsynth>new_fluid_settings"
foreign handler new_fluid_synth ( in tSettings as CUint ) returns CUint binds to "libfluidsynth>new_fluid_synth"
foreign handler new_fluid_audio_driver ( in tSettings as CUint, in tSynth as CUint ) returns CUint binds to "libfluidsynth>new_fluid_audio_driver"
foreign handler fluid_synth_sfload ( in tSynth as CUint, in tFile as ZStringNative, in tPresets as CUint ) returns CUint binds to "libfluidsynth>fluid_synth_sfload"
foreign handler fluid_synth_noteon ( in tSynth as CUint, in tChan as CUint, in tKey as CUint, in tVel as CUint ) returns CUint binds to "libfluidsynth>fluid_synth_noteon"
foreign handler fluid_synth_noteoff ( in tSynth as CUint, in tChan as CUint, in tKey as CUint ) returns CUint binds to "libfluidsynth>fluid_synth_noteoff"

public handler NewFluidSettings ( ) returns Integer
	return new_fluid_settings ( )
end handler

public handler NewFluidSynth ( in tSettings as Integer ) returns Integer
	return new_fluid_synth ( tSettings )
end handler

public handler NewFluidAudioDriver ( in tSettings as Integer, in tSynth as Integer ) returns Integer
	return new_fluid_audio_driver ( tSettings, tSynth )
end handler

public handler FluidSynthSFLoad ( in tSynth as Integer, in tFile as String, in tPresets as Integer  ) returns Integer
	return fluid_synth_sfload ( tSynth, tFile , tPresets )
end handler

public handler FluidSynthNoteOn ( in tSynth as Integer, in tChan as Integer, in tKey as Integer, in tVel as Integer ) returns Integer
	return fluid_synth_noteon ( tSynth, tChan, tKey, tVel )
end handler

public handler FluidSynthNoteOff ( in tSynth as Integer, in tChan as Integer, in tKey as Integer ) returns Integer
	return fluid_synth_noteoff ( tSynth, tChan, tKey )
end handler

end library
LCS Code

Code: Select all

global tSettings
global tSynth
global tDriver
global tFont

on test
   put NewFluidSettings (  ) into tSettings
   put NewFluidSynth ( tSettings ) into tSynth
   put NewFluidAudioDriver ( tSettings, tSynth ) into tDriver
   put FluidSynthSFLoad ( tSynth, "test.SF2", 1 ) into tFont
   repeat with tKey = 1 to 127
      FluidSynthNoteOn tSynth, 0, tKey, 80
      wait 1 second
      FluidSynthNoteOff tSynth, 0, tKey
   end repeat
   -- cleanup ie delete synths etc.. here
end test
Now in the "binds to" directive in the foreign handler declaration you could put...

Code: Select all

 binds to "libfolder/libfluidsynth>dll_function_goes_here"
This would mean that the dll was in a folder next to the lcb code called "libfolder"
So this would tell me that you certainly can specify a relative path for the "binds to" directive. I can only assume you can also specify an absolute path too ( but that is not very desirable anyway "

Now I don't propose that this is in any way the correct way to do things. If you look at the C API of fluid synch you will notice that new_fluid_synth ( ) etc... return pointers to custom types. I have been a bad bad man and converted them to UInts, to get them into my LCS. This is not the way to do things at all but hey it works for the purposes of this.

Note this would only work for 32 bit pointers. In a 64 bit system, the pointers would be too large to cast to a UInt and you would lose bits off the end causing all sorts of nasties.

I am, of course, over simplifying it but I too am a non programmer.

NB to play the sample you will need a file called "test.sf2" beside the dylib or in the system folder.

I hope this works for my OSX brethren, but on Win32 it seems OK
Last edited by n.allan on Fri Oct 02, 2015 4:01 pm, edited 1 time in total.

trevordevore
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 1005
Joined: Sat Apr 08, 2006 3:06 pm
Contact:

Re: Foreign Handlers and Custom Types

Post by trevordevore » Thu Oct 01, 2015 5:12 pm

Thanks for the examples n.allan!
Trevor DeVore
ScreenSteps - https://www.screensteps.com

LiveCode Repos - https://github.com/search?q=user%3Atrevordevore+topic:livecode
LiveCode Builder Repos - https://github.com/search?q=user%3Atrevordevore+topic:livecode-builder

LCMark
Livecode Staff Member
Livecode Staff Member
Posts: 1232
Joined: Thu Apr 11, 2013 11:27 am

Re: Foreign Handlers and Custom Types

Post by LCMark » Thu Oct 01, 2015 7:25 pm

@n.allen: If your C library API is returning pointers to custom types, then you are better off binding those parameters to 'Pointer'. If they can return NULL - then use 'optional Pointer'. In terms of DLL placement - we have added initial support for including per-extension code libraries in packages, I'll need to get Ali to chime in exactly how you do this :)

n.allan
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 153
Joined: Mon Mar 12, 2007 12:06 pm

Re: Foreign Handlers and Custom Types

Post by n.allan » Thu Oct 01, 2015 8:13 pm

Thanks for the info @LCMark. I had reservations about posting that code as I knew it was probably not correct but I blame peer pressure ;) It just meant I could create struts and keep track of the addresses or (handles) in LCS.

I just read a bit about the fact that handles in windows ( such as HWND and HDC etc... ) are actually void pointers and only use the lower bytes specifically so they can be cast to unit safely. I know they are not actually addresses in memory as such but I'd hoped that the same idea could be applied to 32 bit memory addresses of objects/structs created in the c library.

I did try to use the Pointer type variables but I had a problem getting them back into LCS for some reason? The compiler threw up domain errors.

I tried simple returning the Pointer to LCS ( resulted domain error )
I tried converting the Pointer variable using "formatted as string" to LCS (domain error)
I also tried converting the Pointer variable to a number variable using "formatted as Number" to LCS ( also domain error )

The only way that I could successfully pass "Pointers" back and forth is by treating them as Unsigned Integers.

PaulDaMacMan
Posts: 683
Joined: Wed Apr 24, 2013 4:53 pm
Contact:

Re: Foreign Handlers and Custom Types

Post by PaulDaMacMan » Mon Oct 05, 2015 6:08 pm

Hey, no pressure here :lol:

I had a quick try of your code on Mac OS X 10.6.8 before I left for work and got message "couldn't load foreign code library" or something to that effect. I don't think dynamic libraries work the same way on Mac. I think the library needs to be built with option for relative paths (from gcc make) for those to work otherwise the lib needs to be in one of the default paths for libs (usr/lib/ , ~/lib , etc.) or you need to set an environmental variable first.
Anyway libfluidsynth.dylib was in the same folder as the .lcb and my test stack. I moved it to loose in my user folder (~/libfluidsynth.dylib) and the message went away. However, it did not play the notes sent by the repeat with my test.sf2 soundfont. I think I need to change the a.driver ("audio.driver") setting from it's default "alsa" to "coreaudio" for it to work on Mac. I probably need to set fluid_synth_setting_str (tSetting,"audio.driver" string, "coreaudio" string) or something. I had to do the same with switches when using FluidSynths from it's command line interface.
My GitHub Repos: https://github.com/PaulMcClernan/
Related YouTube Videos: PlayList

n.allan
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 153
Joined: Mon Mar 12, 2007 12:06 pm

Re: Foreign Handlers and Custom Types

Post by n.allan » Mon Oct 05, 2015 7:19 pm

The script was pretty lame but there might be an issue with the location of your sf2 file. You can check if it loaded ok by checking the return value from the load sf2 function. If I recall it should be > 0. If it is 0 it means that the file wasn't loaded corectly.

I just had a quick skim through the api to give you quick and dirty example but I recall an entry where it said that you don't need to set the audio driver. The default settings should just work. On any platform.

It might be a 64bit 32bit issue? Which dylib did you download? I'm not sure how it works on osx but in windows you can use 32 bit dlls on a 64 but system through the WOW layer.

Did you try this on your "new fangled" virtual windows machine too? Might even work on linux. I would think on linux it would go into the lib folder to be found by livecode.

There are any number of ways of doing this but we are kind of limited to what we can do here until we can dereference and assign pointer types.

PaulDaMacMan
Posts: 683
Joined: Wed Apr 24, 2013 4:53 pm
Contact:

Re: Foreign Handlers and Custom Types

Post by PaulDaMacMan » Mon Oct 05, 2015 9:12 pm

I'll have another crack at it later and add some error checking and maybe try on Linux Mint too. It's not a 64/32bit issue, Mac OS X works differently there too as the 32bit kernel can load 64bit code (but that's another story) and I've successfully used this lib via it's included command line app through LC's shell() function.

Yes fluidsynth is supposed to just work with the first available audio driver it finds, but in reality I've had to tell it to use "coreaudio" when I used it's CLI in the past (could be something screwy with my system as I have tinkered with different audio/midi drivers like FreeMIDI & Jack in the past).

Anyway, at least I can get LC to definitely load a foreign code library (even if it's not in the directory I wanted to store it at). This is further then I've gotten before.

Thanks!
My GitHub Repos: https://github.com/PaulMcClernan/
Related YouTube Videos: PlayList

n.allan
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 153
Joined: Mon Mar 12, 2007 12:06 pm

Re: Foreign Handlers and Custom Types

Post by n.allan » Mon Oct 05, 2015 10:04 pm

I get you...but just because it works on the command line does not necessarily mean it will work with that lcb library. You will notice in the library example that some functions are returning CUint type. This is a terrible hack as the CUint type is not large enough to hold all 64 bits of an address. The problems you are having might stem from the fact that some of the actual pointer in memory is missing. This can cause all sorts of unpredictable behaviour. In this case I imagine it is important to be certain if it's a 32 bit or 64 bit library.

But certainly add some more error checking just be sure that the functions are returning something meaningful.
Hopefully it is something trivial.

PaulDaMacMan
Posts: 683
Joined: Wed Apr 24, 2013 4:53 pm
Contact:

Re: Foreign Handlers and Custom Types

Post by PaulDaMacMan » Mon Oct 05, 2015 10:38 pm

I understand, I should make sure I'm using the 32bit lib in this case... or maybe attempt to use pointers but it seems you didn't have much luck with that.
My GitHub Repos: https://github.com/PaulMcClernan/
Related YouTube Videos: PlayList

monte
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 1564
Joined: Fri Jan 13, 2012 1:47 am
Contact:

Re: Foreign Handlers and Custom Types

Post by monte » Tue Oct 06, 2015 2:15 am

@n.allan how did you work out it had to be in the same folder or you could use a path in your bind string? The only docs I've seen suggests you need to put the libraries in specific folders resources/code/mac etc. I'll try your way and see if I can get it working...
LiveCode User Group on Facebook : http://FaceBook.com/groups/LiveCodeUsers/

n.allan
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 153
Joined: Mon Mar 12, 2007 12:06 pm

Re: Foreign Handlers and Custom Types

Post by n.allan » Tue Oct 06, 2015 7:46 am

I just did trial and error with regards to the path of the dll to find what worked on windows. There is a % PATH% system variable which tells an exe where to look for resources if it cannot find a file. You can add folders to this list.

I also tested the lcb by putting the dll into a folder called "lib" beside my lcb scriot and changed the binds to directive from

Code: Select all

binds to "libfluidsynth> functionName"
To

Code: Select all

binds to "lib/libfluidynth> funtionName"
This worked.

I never checked but other paths could be equally valid such as

Code: Select all

binds to "c:\\libflidsynth> functionName"
Here the dll will be on my c drive root. Or

Code: Select all

binds to "../../libfluidsynth>functionName
That might also be valid. I might check it at some point.

As for the pointer thing. It may make perfect sense to use pointers in an lcb script but we are currently unable to verify their validity as we have no way of debugging the contents of a pointer variable at this point. I know a little bit of c and I do know that when it comes to pointers you have to be pretty sure they are correct. With my method I can check that the pointer is valid at the very least.

peter-b
Posts: 182
Joined: Thu Nov 20, 2014 2:14 pm

Re: Foreign Handlers and Custom Types

Post by peter-b » Tue Oct 06, 2015 11:04 am

n.allan wrote:I get you...but just because it works on the command line does not necessarily mean it will work with that lcb library. You will notice in the library example that some functions are returning CUint type. This is a terrible hack as the CUint type is not large enough to hold all 64 bits of an address. The problems you are having might stem from the fact that some of the actual pointer in memory is missing. This can cause all sorts of unpredictable behaviour. In this case I imagine it is important to be certain if it's a 32 bit or 64 bit library.

But certainly add some more error checking just be sure that the functions are returning something meaningful.
Hopefully it is something trivial.
The UIntSize type is always large enough to hold a pointer, on all the platforms we currently support.
LiveCode Open Source Team — @PeterTBBrett — peter.brett@livecode.com

n.allan
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 153
Joined: Mon Mar 12, 2007 12:06 pm

Re: Foreign Handlers and Custom Types

Post by n.allan » Tue Oct 06, 2015 12:38 pm

That's good then. Hack away boys.

In that case I suppose you could download the 64 bit library and change the binds to string to match the dll.
The 64 bit version I have is called libsynth64.dll

So I would change the binds to directive to

Code: Select all

binds to "libsynth64> functionName"

LCMark
Livecode Staff Member
Livecode Staff Member
Posts: 1232
Joined: Thu Apr 11, 2013 11:27 am

Re: Foreign Handlers and Custom Types

Post by LCMark » Tue Oct 06, 2015 5:02 pm

@n.allan: I misunderstood your original question about not being able to use Pointer...

As it stands, Pointer does not bridge to LiveCode Script - it might at some point, however the idea is that LiveCode Script remains completely 'safe'. Thus it is the general idea the LCB modules produce APIs similar to those to the engine (in functional form at the moment, syntax bindings will appear with Open Language). This means that low-level concerns such as pointers and such are kept beneath script.

For example, LiveCode Script uses names to identify objects such as sockets, and the engine maps these to actual pointers internally. A similar formalism can be used in LCB - using an array perhaps to map names to Pointer. By having this separation, you have no need to pass Pointers through to LiveCode Script and at the script level, you get to manipulate things with nicer (easier to use safely!) abstractions.

There is a slight issue with doing what you are doing at the moment - it is probably fine, but numbers in LiveCode Script cannot represent a 64-bit integer fully (only 56-bits as the internal representation is doubles). So - what you are doing will work on 32-bit, but might not on 64-bit depending on address space layout.

(All the above being said, you are free to do things as you wish of course - but just be aware, at the moment, 64-bit platforms might not function with this method).

n.allan
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 153
Joined: Mon Mar 12, 2007 12:06 pm

Re: Foreign Handlers and Custom Types

Post by n.allan » Tue Oct 06, 2015 5:55 pm

Ok thanks for that @LCMark. By the way do you guys ever sleep?

Yes I understand we should really be interfacing the pointers inside the lcb file and abstract the interface to LCS

Post Reply