Done (OSX) A few interesting issues with NSSpeechSynthesizer

Just starting out? Need help? Post your questions and find answers here.
erion
Enthusiast
Enthusiast
Posts: 128
Joined: Sun Jan 24, 2010 11:12 pm

Done (OSX) A few interesting issues with NSSpeechSynthesizer

Post by erion »

Hi all,

I'm currently trying to implement a speech module in PB. Most things work, however I have a few interesting issues when I try to implement a queue:
1. My didFinishSpeaking function is only called if I don't use a synth function as a parameter to the loop. E.g. if I use isSpeaking, the program just exits or hangs.
2. If my didFinishSpeaking function in the delegate class gets called, the argument values do not mach. E.g. for 'success' I always get 69 and the synth argument's value is not the same as the synth class's I created.

Note:
For this to run, you will need Wilbert's Cocoa lib at http://www.purebasic.fr/english/viewtop ... 19&t=50688
At the moment, the code has a memory leak, as both the synth and the delegate will not be released.

Code: Select all

Global pbNSSpeechSynthesizerDelegateClass, synthDelegate , RunLoop

Procedure.i create(voice.s)
  If voice<>""
    ProcedureReturn CocoaMessage(0, CocoaMessage(0, 0, "NSSpeechSynthesizer alloc"), "initWithVoice:$", @voice)
    Else
  ProcedureReturn CocoaMessage(0, CocoaMessage(0, 0, "NSSpeechSynthesizer alloc"), "init")
EndIf  
EndProcedure

Procedure Speak(synth.i, text.s)
  CocoaMessage(0,synth, "startSpeakingString:$", @text)
  EndProcedure
  
  Procedure Speaking(synth.i)
    ProcedureReturn CocoaMessage(0, synth, "isSpeaking")
    EndProcedure

Procedure availableVoices (Array avArray.s(1))
    Protected i, av.i, vc
    
av=CocoaMessage(0, 0, "NSSpeechSynthesizer availableVoices")
    vc=CocoaMessage(0,av,"count")
    
    If vc
    ReDim avArray(vc-1)
    For i = 0 To vc - 1
avArray(i)=PeekS(CocoaMessage(0, CocoaMessage(0, av, "objectAtIndex:", i), "UTF8String"), -1, #PB_UTF8)
Next i
ProcedureReturn 1
EndIf
    ProcedureReturn 0
    EndProcedure
    
    ProcedureC didFinishSpeaking(synth.i,finishedSpeaking.a)
    Debug "finished: "+Str(finishedSpeaking)+" synth is "+Str(synth)
        EndProcedure
    
    Procedure Init()
    pbNSSpeechSynthesizerDelegateClass = oCreateClass(oClass("NSObject"), "pbNSSpeechSynthesizerDelegateClass")
oClassAddMethod(pbNSSpeechSynthesizerDelegateClass, oSel("speechSynthesizer:didFinishSpeaking:"), @didFinishSpeaking(), "v@:@@")
oRegisterClass(pbNSSpeechSynthesizerDelegateClass)
synthDelegate = CocoaMessage(0, 0, "pbNSSpeechSynthesizerDelegateClass new")
RunLoop=cocoaMessage(0,0,"NSRunLoop currentRunLoop")
    EndProcedure
    
    
    Procedure new(voice.s="")
          Protected synth.i=create(voice)
    
    If synth
cocoaMessage(0,synth,"setDelegate:@",@synthDelegate)
Debug Str(synth)
ProcedureReturn synth
EndIf
ProcedureReturn 0
EndProcedure

Procedure freeSynth(synth)
cocoaMessage(0,synth,"release")
EndProcedure
  
  Procedure Free()
cocoaMessage(0,synthDelegate,"release")
  cocoaMessage(0,pbNSSpeechSynthesizerDelegateClass,"release")
    EndProcedure
  
  Init()
  synth=new()
  Speak(synth,"This is  a test.")
        While 1
CocoaMessage(0, runloop, "run")
Delay(5)
	Wend
Delay(2000)
freeSynth(synth)
Free()
Last edited by erion on Thu Sep 24, 2015 3:02 pm, edited 1 time in total.
To see a world in a grain of sand,
And a heaven in a wild flower,
Hold infinity in the palm of your hand,
And eternity in an hour.

- W. B.

Visit my site, also for PureBasic goodies http://erion.tdrealms.com
erion
Enthusiast
Enthusiast
Posts: 128
Joined: Sun Jan 24, 2010 11:12 pm

Re: (OSX) A few interesting issues with NSSpeechSynthesizer

Post by erion »

I have done some more investigating, unfortunately my issue is still not fully resolved.

1. It seems that NSRunLoop:run will permanently block my application, I will probably have to use a separate thread. I wonder if I could tap into PB's RunLoop, if any.
2. Using Wilbert's oClassName function, I figured out that didFinishSpeaking returns a "pbNSSpeechSynthesizerDelegateClass" as the sender. This is particularly interesting, as the documentation says for the sender parameter:
An NSSpeechSynthesizer object that has stopped speaking into the sound output device.
The second parameter now returns 65 for true. Either there is a third, unmentioned parameter added, which I doubt, or I am using the wrong type for the bool param.

Erion
To see a world in a grain of sand,
And a heaven in a wild flower,
Hold infinity in the palm of your hand,
And eternity in an hour.

- W. B.

Visit my site, also for PureBasic goodies http://erion.tdrealms.com
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: (OSX) A few interesting issues with NSSpeechSynthesizer

Post by wilbert »

The Cocoa tips thread has some examples of dealing with delegates
http://www.purebasic.fr/english/viewtop ... 19&t=50795

For speech there's also another option.
You can use the C functions like SpeakCFString from the Speech Synthesis Manager.
Windows (x64)
Raspberry Pi OS (Arm64)
erion
Enthusiast
Enthusiast
Posts: 128
Joined: Sun Jan 24, 2010 11:12 pm

Re: (OSX) A few interesting issues with NSSpeechSynthesizer

Post by erion »

Huge thanks Wilbert, I'm going to check these out.

Erion
To see a world in a grain of sand,
And a heaven in a wild flower,
Hold infinity in the palm of your hand,
And eternity in an hour.

- W. B.

Visit my site, also for PureBasic goodies http://erion.tdrealms.com
erion
Enthusiast
Enthusiast
Posts: 128
Joined: Sun Jan 24, 2010 11:12 pm

Re: (OSX) A few interesting issues with NSSpeechSynthesizer

Post by erion »

Wilbert,
I checked out the Cocoa methods thread. Apart from not specifying a type for my delegate function parameters and using your methods to create my class, everything seems to be the same.
The speech manager functions are sadly deprecated since 10.8.

My delegate function is called, honouring a sent stop function properly. My only problem seems to be the passed parameters from OS X.

I am not sure why I get back my delegate class, instead of the NSSpeechSynthesizer object as Apple's documentation says.

Debugging the bool parameter, it does not really change, whether I call delay then stop to stop the synth, or just let it finish speaking.

Any ideas?

Erion
To see a world in a grain of sand,
And a heaven in a wild flower,
Hold infinity in the palm of your hand,
And eternity in an hour.

- W. B.

Visit my site, also for PureBasic goodies http://erion.tdrealms.com
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: (OSX) A few interesting issues with NSSpeechSynthesizer

Post by wilbert »

I think your callback procedure should look like this

Code: Select all

ProcedureC didFinishSpeaking(obj, sel, sender, success)

EndProcedure
You forgot the first two arguments.
erion wrote:The speech manager functions are sadly deprecated since 10.8.
Some of them are but not all of them. Here's an example

Code: Select all

ImportC ""
  
  CFSTR(cStr.p-ascii) As "___CFStringMakeConstantString"
  
  ContinueSpeech (chan)
  CopySpeechProperty (chan, property, *object)
  CountVoices (*numVoices)
  DisposeSpeechChannel (chan)
  GetIndVoice (index, *voice)
  GetVoiceDescription (*voice, *info, infoLength)
  NewSpeechChannel (*voice, *chan)
  PauseSpeechAt (chan, whereToPause)
  SetSpeechProperty (chan, property, object)
  SpeakCFString (chan, aString, options)
  SpeechBusy ()
  SpeechBusySystemWide ()
  StopSpeech (chan)
  StopSpeechAt (chan, whereToStop)
  
EndImport

Structure VoiceSpec
  creator.l
  id.l
EndStructure

Structure VoiceDescription
  length.l
  voice.VoiceSpec
  version.l
  nameLen.a
  name.a[63]
  commentLen.a
  comment.a[255]
  gender.w
  age.w
  script.w
  language.w
  region.w
  reserved.l[4]
EndStructure


kSpeechSpeechDoneCallBack = CFSTR("sdcb")


; create a voice channel with voice named Alex

CountVoices(@voiceCount.w)
idx = 0
While idx < voiceCount
  GetIndVoice(idx, @voice.VoiceSpec)
  GetVoiceDescription (voice, @voiceInfo.VoiceDescription, SizeOf(VoiceDescription))
  If PeekS(@voiceInfo\name, voiceInfo\nameLen, #PB_Ascii) = "Alex"
    Break
  EndIf
  idx + 1  
Wend

NewSpeechChannel(@voice, @chan)


; setup the callback

ProcedureC DoneCB(chan, refCon)
  Debug "done"
EndProcedure

CallbackAsNumber = CocoaMessage(0, 0, "NSNumber numberWithInteger:", @DoneCB())
SetSpeechProperty(chan, kSpeechSpeechDoneCallBack, CallbackAsNumber)


; speak a text 

Text = CocoaMessage(0,0,"NSString stringWithString:$", @"This is a test")
SpeakCFString(chan, Text, #Null)

MessageRequester("","")
PB 5.40+

Code: Select all

*a.Integer = dlsym_(#RTLD_DEFAULT, "kSpeechSpeechDoneCallBack")
kSpeechSpeechDoneCallBack = *a\i

; create a voice channel with voice named Alex

CountVoices_(@voiceCount.w)
idx = 0
While idx < voiceCount
  GetIndVoice_(idx, @voice.VoiceSpec)
  GetVoiceDescription_(voice, @voiceInfo.VoiceDescription, SizeOf(VoiceDescription))
  If PeekS(@voiceInfo\name, voiceInfo\nameLen, #PB_Ascii) = "Alex"
    Break
  EndIf
  idx + 1  
Wend

NewSpeechChannel_(@voice, @chan)


; setup the callback

ProcedureC DoneCB(chan, refCon)
  Debug "done"
EndProcedure

CallbackAsNumber = CocoaMessage(0, 0, "NSNumber numberWithInteger:", @DoneCB())
SetSpeechProperty_(chan, kSpeechSpeechDoneCallBack, CallbackAsNumber)


; speak a text 

Text = CocoaMessage(0,0,"NSString stringWithString:$", @"This is a test")
SpeakCFString_(chan, Text, #Null)

MessageRequester("Speaking", "This is a test")
Last edited by wilbert on Fri Oct 09, 2015 5:58 pm, edited 4 times in total.
Windows (x64)
Raspberry Pi OS (Arm64)
erion
Enthusiast
Enthusiast
Posts: 128
Joined: Sun Jan 24, 2010 11:12 pm

Re: (OSX) A few interesting issues with NSSpeechSynthesizer

Post by erion »

Wilbert, you should get an award. Declaring my procedure as you suggested made everything work like a charm. Huge, huge thanks!

Apparently, obj and sel are passed natively via swift and objC, which I should have thought about as something similar is happening in c++.

Erion
To see a world in a grain of sand,
And a heaven in a wild flower,
Hold infinity in the palm of your hand,
And eternity in an hour.

- W. B.

Visit my site, also for PureBasic goodies http://erion.tdrealms.com
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: (OSX) A few interesting issues with NSSpeechSynthesizer

Post by wilbert »

erion wrote:Apparently, obj and sel are passed natively via swift and objC
Yes, it's described in the Objective-C Runtime Programming Guide.
Objective-C Runtime Programming Guide wrote:These arguments give every method implementation explicit information about the two halves of the message expression that invoked it. They’re said to be “hidden” because they aren’t declared in the source code that defines the method. They’re inserted into the implementation when the code is compiled.
Windows (x64)
Raspberry Pi OS (Arm64)
zefiro_flashparty
User
User
Posts: 74
Joined: Fri Mar 04, 2005 7:46 pm
Location: argentina

Re: Done (OSX) A few interesting issues with NSSpeechSynthes

Post by zefiro_flashparty »

link down?
Amd Vishera fx8350 ,16Gbram, Gtx650 ti, 2gb,Win 10pro. 13tbs. 8)
User avatar
RSBasic
Moderator
Moderator
Posts: 1228
Joined: Thu Dec 31, 2009 11:05 pm
Location: Gernsbach (Germany)
Contact:

Re: Done (OSX) A few interesting issues with NSSpeechSynthes

Post by RSBasic »

What link?
Image
Image
Post Reply