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
-
Simon Knight
- Posts: 854
- Joined: Wed Nov 04, 2009 11:41 am
- Location: Gunthorpe, North Lincs, UK
Post
by Simon Knight » Tue Feb 18, 2020 10:14 pm
Hi Paul,
Also, with Objective C FFI and creating global objects there is some situations where you want 'ObjCRetainedId' instead of the regular 'ObjCId'. This is somewhat of a mystery to me,
Its a mystery to me as well and I am mostly following the same "suck it and see" policy!
I think I read that the alloc method call can cause the problem you mention but I find the documentation is quite confusing. The thing that worries me is that my code creates a memory leak but I wrote to Livecode support and the message was don't worry, so who knows?
It would be great if Livecode central provided some documentation that showed the ref count of objects as they get allocated, initialised and assigned to native variables.
best wishes
Skids
-
trevix
- Posts: 960
- Joined: Sat Feb 24, 2007 11:25 pm
- Location: Italy
-
Contact:
Post
by trevix » Wed Feb 19, 2020 1:04 am
Thaaanks so much, Simon. You are great.
I took the liberty to add some comments for the LC dictionary and to return the voice language identifier together with the voice name, so we know better why the speaker (mostly women...?) sometime is so bad.
Here is it with my additions:
Code: Select all
/**
Speak aloud some text on iOS. A function that take a string and speak it
Name: smkSpeakText
Type: function
*/
library net.anvic.skids.speechsynth
use com.livecode.foreign
use com.livecode.objc
metadata title is "Speech Synth Library"
metadata author is "Simon (Skids) Knight"
metadata version is "1.0.0"
metadata OS is "iOS"
-- bind the memory allocation handler - ok
private foreign handler ObjC_AVSpeechSynthAlloc() \
returns optional ObjcID \
binds to "objc:AVFoundation>AVSpeechSynthesizer.+alloc"
-- bind the initialisation of new object - ok
private foreign handler ObjC_AVSpeechSynthInit(in pObj as ObjcID) \
returns optional ObjcRetainedID \
binds to "objc:AVFoundation>AVSpeechSynthesizer.-init"
-- Now create an 'Utterance' to be passed to the AVSpeechSynthesizer
private foreign handler ObjC_AVSpeechUtteranceAlloc() \
returns optional ObjcID \
binds to "objc:AVFoundation>AVSpeechUtterance.+alloc"
private foreign handler ObjC_AVSpeechUtteranceInitWithString(in pObj as ObjcID, in pText as objcID) \
returns optional ObjcRetainedID \
binds to "objc:AVFoundation>AVSpeechUtterance.-initWithString:"
private foreign handler ObjC_AVSpeechUtteranceSetVoice(in pUtteranceObj as ObjcID, in pVoiceObject as objcID) \
returns optional ObjcRetainedID \
binds to "objc:AVFoundation>AVSpeechUtterance.setVoice:"
-- Now the method to send the utterance to the AVSpeechSynthesizer
private foreign handler ObjC_SpeakUtterance(in pObj as ObjcID, in pUtterance as ObjcID) \
returns nothing \
binds to "objc:AVFoundation>AVSpeechSynthesizer.-speakUtterance:"
--
-- return a list of voices in an array of AVSpeechSynthesisVoice objects
private foreign handler ObjC_AVSpeechSynthesisVoices() \
returns ObjcID \
binds to "objc:AVFoundation>AVSpeechSynthesisVoice.+speechVoices"
private foreign handler Objc_NameOfVoiceNSstring (in pObj as objcID) \
returns ObjcID \
binds to "objc:AVFoundation>AVSpeechSynthesisVoice.name"
private foreign handler Objc_LanguageOfVoiceNSstring (in pObj as objcID) \
returns ObjcID \
binds to "objc:AVFoundation>AVSpeechSynthesisVoice.language"
/**
Summary: Get a list of available voices
**/
variable sVoicesList as List -- of objcObjects that should stay available between calls
public handler smkListOfVoices() returns String
// Note this should always be called even if LCS knows the name of a voice
// it wants to use.
variable tArrayOfVoices as objcObject // array of AVSpeechSythesisVoice Objects
variable tVoices as String
unsafe
put ObjC_AVSpeechSynthesisVoices() into tArrayOfVoices
put AV_ReadVoices(tArrayOfVoices) into tVoices
end unsafe
return tVoices
end handler
private handler AV_ReadVoices(in pNSArrayOfVoices as objcID) returns String
variable tVoicesList as List -- native List LCB type
variable tVoiceObj as objcObject -- AVSpeechSythesisVoice object
variable tNameObj as objcObject -- NSString
variable tLanguageObj as objcObject -- NSString
variable tFoundVoices as String -- native string that will be returned
if pNSArrayOfVoices is not nothing then
put listFromNSArray(pNSArrayOfVoices) into sVoicesList
else
return "Empty Array Passed into AV_ReadVoices"
end if
-- Loop through the voice objects in the list reading the name
repeat for each element tVoiceObj in sVoicesList
unsafe
if Objc_NameOfVoiceNSstring(tVoiceObj) is not nothing then
put Objc_NameOfVoiceNSstring(tVoiceObj) into tNameObj
put Objc_LanguageOfVoiceNSstring(tVoiceObj) into tLanguageObj
put StringFromNSString(tLanguageObj) & "," & StringFromNSString(tNameObj) & ";" after tFoundVoices
end if
end unsafe
end repeat
-- tidy up return string
if tFoundVoices ends with ";" then
delete the last char of tFoundVoices
end if
return tFoundVoices
end handler
/**
Summary: Speak some text, specifying the voice to be used.
smkSpeakText("hello World","Daniel")
Type: function
**/
public handler smkSpeakText(in pText as String, in pVoiceName as String) returns String
variable tSpeechSynth as objcObject
variable tUtterance as objcObject
variable tTextToSpeak as objcObject
variable tVoiceObj as objcObject
-- call function to look up and return voice object
put GetVoiceObjectNamed(pVoiceName) into tVoiceObj
if tVoiceObj is nothing then
return " error getting voice object to use"
end if
unsafe
-- convert the string passed into a NSString object
put StringToNSString(pText) into tTextToSpeak
-- create instance of SpeechSynth
put ObjC_AVSpeechSynthAlloc() into tSpeechSynth
put ObjC_AVSpeechSynthInit(tSpeechSynth) into tSpeechSynth
-- create instance of Utterance
put ObjC_AVSpeechUtteranceAlloc() into tUtterance
put ObjC_AVSpeechUtteranceInitWithString(tUtterance,tTextToSpeak) into tUtterance
-- new code to set voice
ObjC_AVSpeechUtteranceSetVoice(tUtterance, tVoiceObj)
-- Lastly Speak the Utterance
ObjC_SpeakUtterance(tSpeechSynth,tUtterance)
end unsafe
return "Complete"
end handler
private handler GetVoiceObjectNamed(in pVoiceName as String) returns objcObject
variable tVoiceObj as ObjcObject -- this will be returned
variable tNameObj as ObjcObject -- An NSstring object
variable tVoiceName as String
-- Loop through the voice objects in the list reading the name
repeat for each element tVoiceObj in sVoicesList
unsafe
if Objc_NameOfVoiceNSstring(tVoiceObj) is not nothing then
put Objc_NameOfVoiceNSstring(tVoiceObj) into tNameObj
put StringFromNSString(tNameObj) into tVoiceName
if tVoiceName is pVoiceName then
-- correct object found so stop repeating
exit repeat
end if
else
-- an error has occured
return nothing
end if
end unsafe
end repeat
-- this will either return the correct voice or the last voice in list
return tVoiceObj
end handler
end library
I had to use the "|" itemdelimiter because I couldn't find out how to add a TAB (sigh). Anyway, easy to parse in LCS
Trevix
OSX 14.3.1 xCode 15 LC 10 DP7 iOS 15> Android 7>
-
PaulDaMacMan
- Posts: 627
- Joined: Wed Apr 24, 2013 4:53 pm
-
Contact:
Post
by PaulDaMacMan » Wed Feb 19, 2020 3:17 am
trevix wrote: ↑Wed Feb 19, 2020 1:04 am
I had to use the "|" itemdelimiter because I couldn't find out how to add a TAB (sigh). Anyway, easy to parse in LCS
for 'tab' you can use a token \t
like this:
Code: Select all
variable tStr as String -- LCB string
put "\t" into tStr -- tab character
unsafe
put ObjC_NSArrayComponentsJoinedByString(pNSArray, StringToNSString(tStr)) into tNSString -- <-- this is from some other LCB thing I was working on
end unsafe
return StringFromNSString(tNSString) -- returns a tab delimited list
"\r\n" would be the equivalent of LC's CR & LF for a macOS line delimited list