NativeBoost Questions while wrapping FMOD

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
18 messages Options
Reply | Threaded
Open this post in threaded view
|

NativeBoost Questions while wrapping FMOD

Sean P. DeNigris
Administrator
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
Reply | Threaded
Open this post in threaded view
|

Re: NativeBoost Questions while wrapping FMOD

philippeback
We'll soon be able to do this in Pharo then :-)

My friend David also uses FMOD in there.


---
Philippe Back
Dramatic Performance Improvements
Mob: +32(0) 478 650 140 | Fax: +32 (0) 70 408 027
Blog: http://philippeback.be | Twitter: @philippeback

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 <[hidden email]> 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.

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.



Reply | Threaded
Open this post in threaded view
|

Re: NativeBoost Questions while wrapping FMOD

Igor Stasenko
In reply to this post by Sean P. DeNigris



On 21 November 2013 22:40, Sean P. DeNigris <[hidden email]> 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.
Reply | Threaded
Open this post in threaded view
|

Re: NativeBoost Questions while wrapping FMOD

Sean P. DeNigris
Administrator
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
Reply | Threaded
Open this post in threaded view
|

Re: NativeBoost Questions while wrapping FMOD

Igor Stasenko



On 22 November 2013 01:23, Sean P. DeNigris <[hidden email]> 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.
Reply | Threaded
Open this post in threaded view
|

Re: NativeBoost Questions while wrapping FMOD

Sean P. DeNigris
Administrator
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.

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
Reply | Threaded
Open this post in threaded view
|

Re: NativeBoost Questions while wrapping FMOD

Igor Stasenko



On 22 November 2013 05:09, Sean P. DeNigris <[hidden email]> 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 class>>new
  | 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.
Reply | Threaded
Open this post in threaded view
|

Re: NativeBoost Questions while wrapping FMOD

Igor Stasenko

So, here the changes which should work (but i didn't tested).

And being on your place, i would get rid of
FMOD_SYSTEM class,
because you can just subclass  FmodSystem from NBExternalObject directly (and use it in signatures)
and so it will be holding the handle, and naturally work as a representation of external entity.
Also note that in this case
FmodSystem is alias to C  FMOD_SYSTEM*

so, for every function which expects FMOD_SYSTEM*, you should just use FmodSystem,
and if it expects FMOD_SYSTEM**, you using FmodSystem*

basically, same as if you would have:

typedef FmodSystem FMOD_SYSTEM*;

in C.


On 22 November 2013 15:10, Igor Stasenko <[hidden email]> wrote:



On 22 November 2013 05:09, Sean P. DeNigris <[hidden email]> 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 class>>new
  | 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.



--
Best regards,
Igor Stasenko.

FmodSystem.st (6K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: NativeBoost Questions while wrapping FMOD

Stéphane Ducasse
In reply to this post by Igor Stasenko
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 <[hidden email]> 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.

Reply | Threaded
Open this post in threaded view
|

Re: NativeBoost Questions while wrapping FMOD

Stéphane Ducasse

On Nov 23, 2013, at 9:07 AM, Stéphane Ducasse <[hidden email]> wrote:

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.

And igor do not tell me that only C programmer should use NativeBoost.
We should focus on our clients and our clients are smart Pharoers that can learn fast.


Stef




On 21 November 2013 22:40, Sean P. DeNigris <[hidden email]> 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.


Reply | Threaded
Open this post in threaded view
|

Re: NativeBoost Questions while wrapping FMOD

kilon.alios
However we should acknowledge here that using a FFI, any FFI , in any programming language has the mandatory requirement of knowing C. I certainly do believe that documenting Nativeboost is extremely important for helping people to port C libraries to pharo and making Pharo far more useful but we should not kid ourselves. Coding with Nativeboost even though its inside Pharo , even though its smalltalk syntax, even though it has the tools to make your life easier, its still C coding or even Assembly coding if you are brave enough to use the inline assembler. 

I want to help with documentation the problem is that I dont know a lot of stuff about Nativeboost besides the basic pointer things I did with NBOpenGL. 


On Sat, Nov 23, 2013 at 10:10 AM, Stéphane Ducasse <[hidden email]> wrote:

On Nov 23, 2013, at 9:07 AM, Stéphane Ducasse <[hidden email]> wrote:

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.

And igor do not tell me that only C programmer should use NativeBoost.
We should focus on our clients and our clients are smart Pharoers that can learn fast.


Stef




On 21 November 2013 22:40, Sean P. DeNigris <[hidden email]> 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.



Reply | Threaded
Open this post in threaded view
|

Re: NativeBoost Questions while wrapping FMOD

Denis Kudriashov
In reply to this post by Igor Stasenko
Hi

2013/11/22 Igor Stasenko <[hidden email]>
 
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)
Reply | Threaded
Open this post in threaded view
|

Re: NativeBoost Questions while wrapping FMOD

Igor Stasenko



On 23 November 2013 17:42, Denis Kudriashov <[hidden email]> wrote:
Hi

2013/11/22 Igor Stasenko <[hidden email]>
 
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)

of course, the #nbCall: is just a convenience method and meant to be overridden if necessary.

nbCall: fnSpec
    " you can override this method if you need to"
   
    ^ (self nbCalloutIn: thisContext sender)
        convention: self nbCallingConvention;
        function: fnSpec module: self nbLibraryNameOrHandle

And  nbCallingConvention, nbLibraryNameOrHandle is there for override as well.

A more verbose form is:

self nbCallout
        stdcall;
        options: #( - optDirectProxyFnAddress optAllowExternalAddressPtr);
        function: #( NBBootstrapUlong HeapAlloc (ulong heapHandle , 0 , SIZE_T size) ) module: #Kernel32

But be aware though, that this code usually invoked once during code generation,
and later no method code is invoked because it just runs a primitive (primitiveNativeCall), and if it succeeds, it just returns with result, without running a method's body.
So, nbcall can be changed/extended if you need extra thing at code generation stage,
or you want to handle primitive error(s) differently.
But not for handling things like function return values (which happens to be a library's error/success code).

--
Best regards,
Igor Stasenko.
Reply | Threaded
Open this post in threaded view
|

Re: NativeBoost Questions while wrapping FMOD

Igor Stasenko
In reply to this post by Stéphane Ducasse



On 23 November 2013 09:07, Stéphane Ducasse <[hidden email]> wrote:
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 <[hidden email]> 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.
Reply | Threaded
Open this post in threaded view
|

Re: NativeBoost Questions while wrapping FMOD

Stéphane Ducasse

On 23 November 2013 09:07, Stéphane Ducasse <[hidden email]> wrote:
I really think that NativeBoost MUST HAVE a decent documentation.

i agree. i need to invest into it.

we should build a working group with pomodoro around it.
JB and Damien could help.
I can play the naive idiot and editor but I cannot be the driven force because the gap to learn will cost me too much energy.
But this is on my todo to read the tutorial of laurent.


Stef
 
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 <[hidden email]> 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.

Reply | Threaded
Open this post in threaded view
|

Re: NativeBoost Questions while wrapping FMOD

Sean P. DeNigris
Administrator
In reply to this post by Sean P. DeNigris
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...

========
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?

Thanks!
Cheers,
Sean
Reply | Threaded
Open this post in threaded view
|

Re: NativeBoost Questions while wrapping FMOD

Igor Stasenko



On 25 November 2013 21:53, Sean P. DeNigris <[hidden email]> 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.
Reply | Threaded
Open this post in threaded view
|

Re: NativeBoost Questions while wrapping FMOD

Luc Fabresse
In reply to this post by Sean P. DeNigris
Hi Sean,

2013/11/25 Sean P. DeNigris <[hidden email]>
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.