LCB FFI "StringToNSString" not declared

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

Post Reply
trevix
Posts: 958
Joined: Sat Feb 24, 2007 11:25 pm
Location: Italy
Contact:

LCB FFI "StringToNSString" not declared

Post by trevix » Tue Feb 11, 2020 6:27 pm

LC 9.6.0 DP2, OSX 10.14.6
Hi all.
While I know so little about LCB, I tried anyway to put to use the FFI example that I found here:
about building a library that should do text to speech.
So I put together what is below.
My problem is that LCB report an error on the StringToNSString function: Identifier 'StringToNSString' not declared.
What don't I understand?

Code: Select all

library community.livecode.trevix.myspeech
metadata version is "1.0.0"
metadata author is "trevix"
metadata title is "MySpeech"
/**
summary: Use this to speak some text
pString: a text to speech
Returns: return the speech
Description: Take a  string  and return the speech
**/

foreign handler Objc_NSSpeechSynthesizerAlloc() returns ObjcRetainedId binds to "objc:NSSpeechSynthesizer.+alloc"

foreign handler Objc_NSSpeechSynthesizerInitWithVoice(in pSynthesizer as ObjcRetainedId, in pVoice as optional ObjcId) returns optional ObjcRetainedId binds to "objc:NSSpeechSynthesizer.-initWithVoice:"

handler CreateSpeechSynthesizer() returns ObjcObject
   variable tSynthesizer as ObjcObject
   put Objc_NSSpeechSynthesizerAlloc() into tSynthesizer

   return Objc_NSSpeechSynthesizerInitWithVoice(tSynthesizer,nothing)
end handler

foreign handler Objc_NSSpeechSynthesizerStartSpeaking(in pSynthesizer as ObjcId, in pString as ObjcId) returns CBool binds to "objc:NSSpeechSynthesizer.-startSpeakingString:"

private variable mSynthesizer as optional ObjcObject
public handler SynthesizeSpeech(in pString as String) returns Boolean
    if mSynthesizer is nothing then
        put CreateSpeechSynthesizer() into mSynthesizer
    end if

    Objc_NSSpeechSynthesizerStartSpeaking(mSynthesizer, StringToNSString(pString))
end handler

end library
Trevix
OSX 14.3.1 xCode 15 LC 10 DP7 iOS 15> Android 7>

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

Re: LCB FFI "StringToNSString" not declared

Post by trevordevore » Tue Feb 11, 2020 6:31 pm

You need to include the necessary LCB libraries.

Code: Select all

use com.livecode.foreign
use com.livecode.objc
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

trevix
Posts: 958
Joined: Sat Feb 24, 2007 11:25 pm
Location: Italy
Contact:

Re: LCB FFI "StringToNSString" not declared

Post by trevix » Tue Feb 11, 2020 6:52 pm

Thankyou.
Now I've this:
error: Unsafe handler 'Objc_NSSpeechSynthesizerStartSpeaking' can only be called in unsafe context
Objc_NSSpeechSynthesizerStartSpeaking(mSynthesizer, StringToNSString(pString))

Code: Select all

library community.livecode.trevix.myspeech
use com.livecode.foreign
use com.livecode.objc

metadata version is "1.0.0"
metadata author is "trevix"
metadata title is "MySpeech"
/**
summary: Use this to speak some text
pString: a text to speech
Returns: return the speech
Description: Take a  string  and return the speech
**/


foreign handler Objc_NSSpeechSynthesizerAlloc() returns ObjcRetainedId binds to "objc:NSSpeechSynthesizer.+alloc"

foreign handler Objc_NSSpeechSynthesizerInitWithVoice(in pSynthesizer as ObjcRetainedId, in pVoice as optional ObjcId) returns optional ObjcRetainedId binds to "objc:NSSpeechSynthesizer.-initWithVoice:"

handler CreateSpeechSynthesizer() returns ObjcObject
   variable tSynthesizer as ObjcObject
   put Objc_NSSpeechSynthesizerAlloc() into tSynthesizer

   return Objc_NSSpeechSynthesizerInitWithVoice(tSynthesizer,nothing)
end handler

foreign handler Objc_NSSpeechSynthesizerStartSpeaking(in pSynthesizer as ObjcId, in pString as ObjcId) returns CBool binds to "objc:NSSpeechSynthesizer.-startSpeakingString:"

private variable mSynthesizer as optional ObjcObject
public handler SynthesizeSpeech(in pString as String) returns Boolean
    if mSynthesizer is nothing then
        put CreateSpeechSynthesizer() into mSynthesizer
    end if

    Objc_NSSpeechSynthesizerStartSpeaking(mSynthesizer, StringToNSString(pString))
end handler

end library
Trevix
OSX 14.3.1 xCode 15 LC 10 DP7 iOS 15> Android 7>

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

Re: LCB FFI "StringToNSString" not declared

Post by trevordevore » Tue Feb 11, 2020 7:00 pm

You need to wrap your code in an unsafe/end unsafe block. Take a look at code from my GitHub repo for examples:

https://github.com/trevordevore/lc-maco ... /nsapp.lcb
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

trevix
Posts: 958
Joined: Sat Feb 24, 2007 11:25 pm
Location: Italy
Contact:

Re: LCB FFI "StringToNSString" not declared

Post by trevix » Tue Feb 11, 2020 7:30 pm

Ok. Now it works (see below for anyone interested). Thankyou.
How hard will it be for me to do the same for iOS?

Code: Select all

library community.livecode.trevix.myspeech
use com.livecode.foreign
use com.livecode.objc

metadata version is "1.0.0"
metadata author is "trevix"
metadata title is "MySpeech"
/**
summary: Use this to speak some text
pString: a text to speech
Returns: return the speech
Description: Take a  string  and return the speech
**/

foreign handler Objc_NSSpeechSynthesizerAlloc() returns ObjcRetainedId binds to "objc:NSSpeechSynthesizer.+alloc"
foreign handler Objc_NSSpeechSynthesizerInitWithVoice(in pSynthesizer as ObjcRetainedId, in pVoice as optional ObjcId) returns optional ObjcRetainedId binds to "objc:NSSpeechSynthesizer.-initWithVoice:"
foreign handler Objc_NSSpeechSynthesizerStartSpeaking(in pSynthesizer as ObjcId, in pString as ObjcId) returns CBool binds to "objc:NSSpeechSynthesizer.-startSpeakingString:"

handler CreateSpeechSynthesizer() returns ObjcObject
   variable tSynthesizer as ObjcObject
   unsafe
   put Objc_NSSpeechSynthesizerAlloc() into tSynthesizer
   return Objc_NSSpeechSynthesizerInitWithVoice(tSynthesizer,nothing)
   end unsafe
end handler

private variable mSynthesizer as optional ObjcObject

public handler SynthesizeSpeech(in pString as String) returns Boolean
   unsafe
    if mSynthesizer is nothing then
        put CreateSpeechSynthesizer() into mSynthesizer
    end if
    Objc_NSSpeechSynthesizerStartSpeaking(mSynthesizer, StringToNSString(pString))
    end unsafe
end handler

end library
Trevix
OSX 14.3.1 xCode 15 LC 10 DP7 iOS 15> Android 7>

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

Re: LCB FFI "StringToNSString" not declared

Post by trevordevore » Tue Feb 11, 2020 7:49 pm

It's been a while since I've used LCB on iOS but it should be the same if you are using Objective-C libraries included with iOS.
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

trevix
Posts: 958
Joined: Sat Feb 24, 2007 11:25 pm
Location: Italy
Contact:

Re: LCB FFI "StringToNSString" not declared

Post by trevix » Sun Feb 16, 2020 6:15 pm

In order to complete the example of OSX Text To Speech found in https://livecode.com/docs/9-5-0/extendi ... -livecode/, I added, to the above LCB script the following (supposed to report when the speech has come to an end. It loads OK):

Code: Select all

--TEST THE END OF SPEECH
foreign handler Objc_SpeechSynthesizerDidFinishSpeaking(in pSynthesizerDelegate as ObjcId, in pSynthesizer as ObjcId, in pFinished as CBool) \
 returns nothing \
       binds to "objc:.-speechSynthesizer:didFinishSpeaking:"

handler OnDidFinishSpeaking(in pSynthesizerDelegate as ObjcId, in pSynthesizer as ObjcId, in pFinished as CBool) \
   returns nothing
   post "finishedSpeaking"
end handler

foreign handler Objc_SpeechSynthesizerSetDelegate(in pSynthesizer as ObjcId, in pDelegate as ObjcId) \
   returns nothing \
   binds to "objc:NSSpeechSynthesizer.-setDelegate:"

unsafe handler SetSpeechSynthesizerDelegate()
       variable tDelegate as ObjcObject

       put CreateObjcInformalDelegate([Objc_SpeechSynthesizerDidFinishSpeaking], \
           { "speechSynthesizer:didFinishSpeaking:": \
             OnDidFinishSpeaking }) into tDelegate
       Objc_SpeechSynthesizerSetDelegate(mSynthesizer, tDelegate)
end handler
--END TEST THE END OF SPEECH
Problems:
1 - I don't understand (and find) how the line post "finishedSpeaking" should report to the LCS stack (or the calling script?).
On the LCB language reference there is a mention to "dispatch", but I don't think this is part of the LCB language

2 - in the linked example, the public handle "SynthesizeSpeech" (used in the main script from LCS in order to get the computer to speak) returns an error in LCS:
Message execution error:
Error description: LCB Error in file TestOSXspeech.lcb at line 73: Value is not of correct type for return - expected type <type: livecode.lang.boolean> when returning from community.livecode.trevix.testosxspeech.SynthesizeSpeech
Hint: runtime
Unless I add:

Code: Select all

return "Complete"
that get passed regularly to LSC. Is this part of the problem for making post "finishedSpeaking" working? (I tried any kind of handler and position in LCS to make it work, to no avail)

Thanks for any help
Trevix
OSX 14.3.1 xCode 15 LC 10 DP7 iOS 15> Android 7>

trevix
Posts: 958
Joined: Sat Feb 24, 2007 11:25 pm
Location: Italy
Contact:

Re: LCB FFI "StringToNSString" not declared

Post by trevix » Sun Feb 16, 2020 6:44 pm

Ok: I fixed point 2 like this:

Code: Select all

if Objc_NSSpeechSynthesizerStartSpeaking(mSynthesizer, StringToNSString(pString)) then
    return true
end if
Trevix
OSX 14.3.1 xCode 15 LC 10 DP7 iOS 15> Android 7>

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

Re: LCB FFI "StringToNSString" not declared

Post by trevordevore » Mon Feb 17, 2020 2:28 pm

1) `post` in LCB is the equivalent of `dispatch` in LCS. You just need to define a `finishedSpeaking ` command in your test stack to process the message.

2) In LCB you need to return the type defined in the handler definition. So `SynthesizeSpeech` just return a Boolean value. Since `Objc_NSSpeechSynthesizerStartSpeaking` returns a Boolean just return that value from `SynthesizeSpeech`.

Code: Select all

return Objc_NSSpeechSynthesizerStartSpeaking(mSynthesizer, StringToNSString(pString))
I believe CBool will automatically be converted to Boolean but I can't test at the moment. I you get any errors then just create a script local boolean variable, store the result of `Objc_NSSpeechSynthesizerStartSpeaking` and the return the script local variable.
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

trevix
Posts: 958
Joined: Sat Feb 24, 2007 11:25 pm
Location: Italy
Contact:

Re: LCB FFI "StringToNSString" not declared

Post by trevix » Mon Feb 17, 2020 9:52 pm

Thanks. You are alway very helpful.
I believe CBool will automatically be converted to Boolean
It does not. But I solved problem 2, so it doesn't matter.

I understand the "post" thing (not thanks to the terrible LCB dictionary).
Problem is that my "OnDidFinishSpeaking" doesn't seem to be triggering.
I tried to re-trace the script sequence using "log" but to no avail.

- Are there other ways to find what is wrong in a LCB scripts (that load fine)
- Someone knows why this particular code doen't work?
- And finally: shouldn't the script examples work and, if not, be updated, when they are posted by the LC HOUSE?

Trevix
Trevix
OSX 14.3.1 xCode 15 LC 10 DP7 iOS 15> Android 7>

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

Re: LCB FFI "StringToNSString" not declared

Post by trevordevore » Mon Feb 17, 2020 10:06 pm

Your code only returns a boolean value if the FFI call succeeds (at least the code you posted). It doesn't return `false` if the FFI call fails. So it may be working in your tests but it needs to be modified to be work correctly in call cases.

Are you calling `SetSpeechSynthesizerDelegate` anywhere in your code after populating the mSynthesizer variable? If not then that would explain why `OnDidFinishSpeaking` isn't being called.
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

Post Reply

Return to “LiveCode Builder”