Re: [Pharo-users] NativeBoost Questions while wrapping FMOD
On 25 November 2013 21:53, Sean P. DeNigris s...@clipperadams.com wrote: Sean P. DeNigris wrote I'm wrapping the FMOD cross-platform audio library [snip] I have a few more questions Problem #1: I have the following callout: primitive: #primitiveNativeCall module: #NativeBoostPlugin FMOD_RESULT FMOD_System_Init( FMOD_SYSTEM *system, int maxchannels, FMOD_INITFLAGS flags, void *extradriverdata); ^ self nbCall: #(FMOD_RESULT System_Init(NBExternalAddress system, 32, 0, nil)). It works fine on Mac. On Windows, it returns some crazy error code that is not listed in the API. However, if I wrap the FMOD DLL in another DLL that just forwards all calls: FMOD_RESULT System_Init(FMOD_SYSTEM *system, int maxchannels, FMOD_INITFLAGS flags, void *extradriverdata) { return FMOD_System_Init(system, maxchannels, flags, extradriverdata); } When I call out to the wrapper DLL, it works! Does that provide a clue as to what's going wrong when calling from NB? This seems like a calling convention issue. By default, unless you specify, NB using cdecl calling convention, but on windows, many libs (especially kernel) using stdcall convention. Check how the library is built and which call convention it uses. Or just try changing it and see if it solves the problem. Other info: - Other dll functions succeed, so there is communication with the library. In fact, if I proceed past that first error, a sound starts to play... but then the VM crashes... Problem #2: (much less important) I wanted to be able to bundle the DLL with the image so one doesn't have to copy it into the VM folder. If I use the full path for the wrapper DLL described above, it is found, but when it calls fmodL.dll, which is in the same directory, it can't be found. I could only get it to work if at least fmodL.dll is in the VM plugins folder. Is there a way to specify more search locations for dynamic libraries from the image side? No, you can do it yourself in a form of: self nbCall: ... module: (self searchAndLoadLibrary) Thanks! - Cheers, Sean -- View this message in context: http://forum.world.st/NativeBoost-Questions-while-wrapping-FMOD-tp4724116p4725188.html Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com. -- Best regards, Igor Stasenko.
Re: [Pharo-users] NativeBoost Questions while wrapping FMOD
Hi Sean, 2013/11/25 Sean P. DeNigris s...@clipperadams.com Sean P. DeNigris wrote I'm wrapping the FMOD cross-platform audio library [snip] I have a few more questions Problem #1: I have the following callout: primitive: #primitiveNativeCall module: #NativeBoostPlugin FMOD_RESULT FMOD_System_Init( FMOD_SYSTEM *system, int maxchannels, FMOD_INITFLAGS flags, void *extradriverdata); ^ self nbCall: #(FMOD_RESULT System_Init(NBExternalAddress system, 32, 0, nil)). It works fine on Mac. On Windows, it returns some crazy error code that is not listed in the API. However, if I wrap the FMOD DLL in another DLL that just forwards all calls: FMOD_RESULT System_Init(FMOD_SYSTEM *system, int maxchannels, FMOD_INITFLAGS flags, void *extradriverdata) { return FMOD_System_Init(system, maxchannels, flags, extradriverdata); } When I call out to the wrapper DLL, it works! Does that provide a clue as to what's going wrong when calling from NB? Other info: - Other dll functions succeed, so there is communication with the library. In fact, if I proceed past that first error, a sound starts to play... but then the VM crashes... could it be a calling convention problem? cdecl, ... Problem #2: (much less important) I wanted to be able to bundle the DLL with the image so one doesn't have to copy it into the VM folder. If I use the full path for the wrapper DLL described above, it is found, but when it calls fmodL.dll, which is in the same directory, it can't be found. I could only get it to work if at least fmodL.dll is in the VM plugins folder. Is there a way to specify more search locations for dynamic libraries from the image side? I do not know if one can add more search locations. But, you can rebuild the full path from image side: Smalltalk imageDirectory / 'lib.dll' Cheers, Luc Thanks! - Cheers, Sean -- View this message in context: http://forum.world.st/NativeBoost-Questions-while-wrapping-FMOD-tp4724116p4725188.html Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com.
Re: [Pharo-users] NativeBoost Questions while wrapping FMOD
I really think that NativeBoost MUST HAVE a decent documentation. It is a central part of Pharo and Igor you should do something about it. If I would know I would have written a chapter on it but I cannot because I DO NOT KNOW. Stef On 21 November 2013 22:40, Sean P. DeNigris s...@clipperadams.com wrote: I'm wrapping the FMOD cross-platform audio library. The code is MIT and lives at http://smalltalkhub.com/#!/~SeanDeNigris/FMOD Problem #1: I'm calling into the FMOD library with: primStoreIsPlaying: channelHandle in: isPlayingHandle primitive: #primitiveNativeCall module: #NativeBoostPlugin FMOD_RESULT FMOD_Channel_IsPlaying( FMOD_CHANNEL *channel, bool *isplaying); ^ self nbCall: #(FMOD_RESULT FMOD_Channel_IsPlaying(NBExternalAddress channel, NBExternalAddress isPlayingHandle)). I call the above with: isPlaying | isPlaying | isPlaying := NBExternalAddress new. self primStoreIsPlaying: channel in: isPlaying. ^ isPlaying value 0. isPlaying is always 0. The method works directly from C with: int isPlaying = 1; while (isPlaying) { FMOD_Channel_IsPlaying(channel, isPlaying); } I also tried changing the callout signature to ... bool* isPlayingHandle) and passing isPlaying := true. instead of using the NBExternalAddress stuff. err.. again, you must pass an address where value will be stored, ^ self nbCall: #(FMOD_RESULT FMOD_Channel_IsPlaying( NBExternalAddress channel, NBExternalAddress * isPlayingHandle)). otherwise you passing NULL pointer, and i quite surprised it not segfaults when you call it like that (looks like they have a check in the library ) you can also use NBExternalTypeValue for that: boolValueClass := NBExternalTypeValue ofType: 'bool'. sure thing, creating anonymous class for each call is overkill, this should be done once, somewhere else boolValue := boolValueClass new. self callTheThingWith: boolValue. boolValue value ifTrue: [... blah] (and this will work, assuming you have bool* in signature for this argument). I have a few more questions, but this is the most pressing as it's holding up any further development. Thanks! - Cheers, Sean -- View this message in context: http://forum.world.st/NativeBoost-Questions-while-wrapping-FMOD-tp4724116.html Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com. -- Best regards, Igor Stasenko.
Re: [Pharo-users] NativeBoost Questions while wrapping FMOD
Hi 2013/11/22 Igor Stasenko siguc...@gmail.com 2. instead of returning useful objects, FMOD returns error codes and you pass a pointer to receive the object. - The first consequence is that I have to wrap all the calls e.g. self processErrorCode: self primCreate. where processErrorCode: anInteger anInteger = 0 ifFalse: [ self error: 'FMOD returned error code ', anInteger asString ]. Is there a more graceful way to do that? i doubt so.. since it is library API which dictates you to use it in certain way. The proper error handling never hurts (except from causing extra code bloat, of course :) Is it good idea to override (or create new) #nbCall: method to handle library common logic? (when any library function returns error code)
Re: [Pharo-users] NativeBoost Questions while wrapping FMOD
On 23 November 2013 09:07, Stéphane Ducasse stephane.duca...@inria.frwrote: I really think that NativeBoost MUST HAVE a decent documentation. i agree. i need to invest into it. It is a central part of Pharo and Igor you should do something about it. If I would know I would have written a chapter on it but I cannot because I DO NOT KNOW. Stef On 21 November 2013 22:40, Sean P. DeNigris s...@clipperadams.com wrote: I'm wrapping the FMOD cross-platform audio library. The code is MIT and lives at http://smalltalkhub.com/#!/~SeanDeNigris/FMOD Problem #1: I'm calling into the FMOD library with: primStoreIsPlaying: channelHandle in: isPlayingHandle primitive: #primitiveNativeCall module: #NativeBoostPlugin FMOD_RESULT FMOD_Channel_IsPlaying( FMOD_CHANNEL *channel, bool *isplaying); ^ self nbCall: #(FMOD_RESULT FMOD_Channel_IsPlaying(NBExternalAddress channel, NBExternalAddress isPlayingHandle)). I call the above with: isPlaying | isPlaying | isPlaying := NBExternalAddress new. self primStoreIsPlaying: channel in: isPlaying. ^ isPlaying value 0. isPlaying is always 0. The method works directly from C with: int isPlaying = 1; while (isPlaying) { FMOD_Channel_IsPlaying(channel, isPlaying); } I also tried changing the callout signature to ... bool* isPlayingHandle) and passing isPlaying := true. instead of using the NBExternalAddress stuff. err.. again, you must pass an address where value will be stored, ^ self nbCall: #(FMOD_RESULT FMOD_Channel_IsPlaying( NBExternalAddress channel, NBExternalAddress * isPlayingHandle)). otherwise you passing NULL pointer, and i quite surprised it not segfaults when you call it like that (looks like they have a check in the library ) you can also use NBExternalTypeValue for that: boolValueClass := NBExternalTypeValue ofType: 'bool'. sure thing, creating anonymous class for each call is overkill, this should be done once, somewhere else boolValue := boolValueClass new. self callTheThingWith: boolValue. boolValue value ifTrue: [... blah] (and this will work, assuming you have bool* in signature for this argument). I have a few more questions, but this is the most pressing as it's holding up any further development. Thanks! - Cheers, Sean -- View this message in context: http://forum.world.st/NativeBoost-Questions-while-wrapping-FMOD-tp4724116.html Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com. -- Best regards, Igor Stasenko. -- Best regards, Igor Stasenko.
Re: [Pharo-users] NativeBoost Questions while wrapping FMOD
On 22 November 2013 05:09, Sean P. DeNigris s...@clipperadams.com wrote: Igor Stasenko wrote The better way is to subclass from NBExternalObject then which made exactly for such purposes, by holding an opaque handle to something (you don't care what is inside), and simplifies a lot of things. I made NBExternalObject subclass: #FMOD_SYSTEM. I then tried to use it via: system := FMOD_SYSTEM new. err := self System_CreateNBExternalObject: system. With callout: ^ self nbCall: #(FMOD_RESULT FMOD_System_Create(NBExternalObject system)). For the argument type in the signature, for good measure I tried: 1. NBExternalObject 2. FMOD_SYSTEM 3. NBExternalAddress All three with zero, one, and two *'s after. The only one that didn't report some variety of An instance of Xyz expected was FMOD_System_Create(FMOD_SYSTEM system) for which the library returns an invalid argument error code. yet again, you miss the right solution: you must pass a pointer to where value will be stored (since function doing exactly that). so you should do it like: 1. NBExternalObject subclass: #FMOD_SYSTEM. 2. method to call the function will look like following: create: system ^ self nbCall: #(FMOD_RESULT FMOD_System_Create(FMOD_SYSTEM * system)). 3. and call it like following: system := FMOD_SYSTEM new. self handleError: (self create: system) ifOk: [ ^ system ] 3a. optionally, you want want to initialize it just after you know that you obtained correct handle, to do that, you could do following: FMOD_SYSTEM classnew | system | system := super new. self handleError: (self create: system) ifOk: [ ^ self newWithHandle: system handle ] and newWithHandle: could look something like following: newWithHandle: aHandle system := super basicNew. system handle: aHandle. ^ system initialize (because it is important to set the handle first, like that you can actually initialize something more by using it, which you logically do, just after creating a new handle. and note you must not call 'super initialize' then, because it will reset handle.) and i'm sure you can find more elegant solution :) btw if anyone wants to play with it: 1. Gofer it smalltalkhubUser: 'SeanDeNigris' project: 'FMOD'; package: 'FMOD'; load. 2. Download the FMOD library for your platform: - windows - http://www.fmod.org/download/fmodstudio/api/Win/fmodstudioapi10208win-installer.exe - Mac - http://www.fmod.org/download/fmodstudio/api/Mac/fmodstudioapi10208mac-installer.dmg 3. Copy the library to FileLocator imageDirectory / 'FMOD Programmers API/api/lowlevel/lib/libfmod.dylib' The working example is: | sound | sound := FmodSound fromFile: '/path/to/file.mp3' asFileReference. [ sound play ] fork. The broken one described above is: FMOD exampleNBExternalObject. - Cheers, Sean -- View this message in context: http://forum.world.st/NativeBoost-Questions-while-wrapping-FMOD-tp4724116p4724192.html Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com. -- Best regards, Igor Stasenko.
Re: [Pharo-users] NativeBoost Questions while wrapping FMOD
We'll soon be able to do this in Pharo then :-) My friend David also uses FMOD in there. http://www.youtube.com/watch?v=077sYFHAgTM --- Philippe Back Dramatic Performance Improvements Mob: +32(0) 478 650 140 | Fax: +32 (0) 70 408 027 Mail:p...@highoctane.be | Web: http://philippeback.eu Blog: http://philippeback.be | Twitter: @philippeback Youtube: http://www.youtube.com/user/philippeback/videos High Octane SPRL rue cour Boisacq 101 | 1301 Bierges | Belgium Pharo Consortium Member - http://consortium.pharo.org/ Featured on the Software Process and Measurement Cast - http://spamcast.libsyn.com Sparx Systems Enterprise Architect and Ability Engineering EADocX Value Added Reseller On Thu, Nov 21, 2013 at 10:40 PM, Sean P. DeNigris s...@clipperadams.comwrote: I'm wrapping the FMOD cross-platform audio library. The code is MIT and lives at http://smalltalkhub.com/#!/~SeanDeNigris/FMOD Problem #1: I'm calling into the FMOD library with: primStoreIsPlaying: channelHandle in: isPlayingHandle primitive: #primitiveNativeCall module: #NativeBoostPlugin FMOD_RESULT FMOD_Channel_IsPlaying( FMOD_CHANNEL *channel, bool *isplaying); ^ self nbCall: #(FMOD_RESULT FMOD_Channel_IsPlaying(NBExternalAddress channel, NBExternalAddress isPlayingHandle)). I call the above with: isPlaying | isPlaying | isPlaying := NBExternalAddress new. self primStoreIsPlaying: channel in: isPlaying. ^ isPlaying value 0. isPlaying is always 0. The method works directly from C with: int isPlaying = 1; while (isPlaying) { FMOD_Channel_IsPlaying(channel, isPlaying); } I also tried changing the callout signature to ... bool* isPlayingHandle) and passing isPlaying := true. instead of using the NBExternalAddress stuff. I have a few more questions, but this is the most pressing as it's holding up any further development. Thanks! - Cheers, Sean -- View this message in context: http://forum.world.st/NativeBoost-Questions-while-wrapping-FMOD-tp4724116.html Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com.
Re: [Pharo-users] NativeBoost Questions while wrapping FMOD
On 21 November 2013 22:40, Sean P. DeNigris s...@clipperadams.com wrote: I'm wrapping the FMOD cross-platform audio library. The code is MIT and lives at http://smalltalkhub.com/#!/~SeanDeNigris/FMOD Problem #1: I'm calling into the FMOD library with: primStoreIsPlaying: channelHandle in: isPlayingHandle primitive: #primitiveNativeCall module: #NativeBoostPlugin FMOD_RESULT FMOD_Channel_IsPlaying( FMOD_CHANNEL *channel, bool *isplaying); ^ self nbCall: #(FMOD_RESULT FMOD_Channel_IsPlaying(NBExternalAddress channel, NBExternalAddress isPlayingHandle)). I call the above with: isPlaying | isPlaying | isPlaying := NBExternalAddress new. self primStoreIsPlaying: channel in: isPlaying. ^ isPlaying value 0. isPlaying is always 0. The method works directly from C with: int isPlaying = 1; while (isPlaying) { FMOD_Channel_IsPlaying(channel, isPlaying); } I also tried changing the callout signature to ... bool* isPlayingHandle) and passing isPlaying := true. instead of using the NBExternalAddress stuff. err.. again, you must pass an address where value will be stored, ^ self nbCall: #(FMOD_RESULT FMOD_Channel_IsPlaying( NBExternalAddress channel, NBExternalAddress * isPlayingHandle)). otherwise you passing NULL pointer, and i quite surprised it not segfaults when you call it like that (looks like they have a check in the library ) you can also use NBExternalTypeValue for that: boolValueClass := NBExternalTypeValue ofType: 'bool'. sure thing, creating anonymous class for each call is overkill, this should be done once, somewhere else boolValue := boolValueClass new. self callTheThingWith: boolValue. boolValue value ifTrue: [... blah] (and this will work, assuming you have bool* in signature for this argument). I have a few more questions, but this is the most pressing as it's holding up any further development. Thanks! - Cheers, Sean -- View this message in context: http://forum.world.st/NativeBoost-Questions-while-wrapping-FMOD-tp4724116.html Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com. -- Best regards, Igor Stasenko.
Re: [Pharo-users] NativeBoost Questions while wrapping FMOD
On 22 November 2013 01:23, Sean P. DeNigris s...@clipperadams.com wrote: Igor Stasenko wrote err.. again, you must pass an address where value will be stored, hee hee... sorry... I don't understand enough of what's going on behind the scenes to adapt well. I reasoned that since last time, the signature was Whatever** and you said to make it NBExternalAddress*, that therefore Whatever* (one less *) would be NBExternalAddress (also one less *). While we're here, I'll ask my other questions... 1. What's the differenct between primitive: #primitiveNativeCall module: #NativeBoostPlugin and the variant with error:? What does the second one buy you? Historically, NB was first running on Squeak VM, which has no support for primitive error. Using extra #error: keyword in primitive is HIGHLY recommended, unless you want to deal with some quite rare (but possible and thus very hard to track down) wrong error reporting. 2. instead of returning useful objects, FMOD returns error codes and you pass a pointer to receive the object. - The first consequence is that I have to wrap all the calls e.g. self processErrorCode: self primCreate. where processErrorCode: anInteger anInteger = 0 ifFalse: [ self error: 'FMOD returned error code ', anInteger asString ]. Is there a more graceful way to do that? i doubt so.. since it is library API which dictates you to use it in certain way. The proper error handling never hurts (except from causing extra code bloat, of course :) - The second issue is how to create a Smalltalk object from the pointer. What I've been doing is: | soundHandle | soundHandle := NBExternalAddress new. self processErrorCode: (self primCreate: soundHandle on: system handle fromFile: file fullName). sound := FmodSystemSound on: soundHandle. Again, is there a better way? I thought to subclass NBExternalAddress, but evaluating sound := FmodSystemSound new to pass to the callout seemed a bit dirty i.e. it is not a invalid instance until initialized by the callout. I also played around with NBExternalObject, but couldn't get that to work either... The better way is to subclass from NBExternalObject then which made exactly for such purposes, by holding an opaque handle to something (you don't care what is inside), and simplifies a lot of things. Thanks for all the support. This is fun!! You are very welcome. - Cheers, Sean -- View this message in context: http://forum.world.st/NativeBoost-Questions-while-wrapping-FMOD-tp4724116p4724158.html Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com. -- Best regards, Igor Stasenko.
Re: [Pharo-users] NativeBoost Questions while wrapping FMOD
Igor Stasenko wrote err.. again, you must pass an address where value will be stored, hee hee... sorry... I don't understand enough of what's going on behind the scenes to adapt well. I reasoned that since last time, the signature was Whatever** and you said to make it NBExternalAddress*, that therefore Whatever* (one less *) would be NBExternalAddress (also one less *). While we're here, I'll ask my other questions... 1. What's the differenct between primitive: #primitiveNativeCall module: #NativeBoostPlugin and the variant with error:? What does the second one buy you? 2. instead of returning useful objects, FMOD returns error codes and you pass a pointer to receive the object. - The first consequence is that I have to wrap all the calls e.g. self processErrorCode: self primCreate. where processErrorCode: anInteger anInteger = 0 ifFalse: [ self error: 'FMOD returned error code ', anInteger asString ]. Is there a more graceful way to do that? - The second issue is how to create a Smalltalk object from the pointer. What I've been doing is: | soundHandle | soundHandle := NBExternalAddress new. self processErrorCode: (self primCreate: soundHandle on: system handle fromFile: file fullName). sound := FmodSystemSound on: soundHandle. Again, is there a better way? I thought to subclass NBExternalAddress, but evaluating sound := FmodSystemSound new to pass to the callout seemed a bit dirty i.e. it is not a invalid instance until initialized by the callout. I also played around with NBExternalObject, but couldn't get that to work either... Thanks for all the support. This is fun!! - Cheers, Sean -- View this message in context: http://forum.world.st/NativeBoost-Questions-while-wrapping-FMOD-tp4724116p4724158.html Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com.