1

I am writing a morse code command line tool for Mac OS X written with Swift as a scripting language. I want to give the user the option to hear the morse code using Apple's NSSpeechSynthesizer. I can get this to work inside an app no sweat. But inside a command line tool or a Swift script the startSpeakingString() function is not audible--unless I step through the code line by line.

Here is the code in Swift (it's the same for a proper command line tool or a script)

import Foundation
import AppKit

var synth:NSSpeechSynthesizer = NSSpeechSynthesizer.init()
synth.startSpeakingString("dit dah")

And here is the code in an Objective-C command line tool

@import Foundation;
@import AppKit;

int main(int argc, const char * argv[]) {
    @autoreleasepool {
         NSSpeechSynthesizer *synth = [[NSSpeechSynthesizer alloc] init];
         [synth startSpeakingString:@"dit dah"];
    }
    return 0
}

The NSSpeechSythesizer instance seems legit in all cases. The startSpeakingString() function returns true in all cases. Here is my repo (work in progress): https://github.com/jpavley/swift-scripts

John Pavley
  • 5,366
  • 2
  • 14
  • 16
  • Your program needs a "run loop". See http://stackoverflow.com/questions/25126471/cfrunloop-in-swift-command-line-program for various solutions. – Martin R Apr 19 '15 at 16:09
  • More run loop solutions here: http://stackoverflow.com/questions/28590701/multiple-workers-in-swift-command-line-tool. – Martin R Apr 19 '15 at 16:19
  • Yes thank you! And after fooling around I can get the tool to output speech and then it runs forever. But I found creating a delegate, running it, and sleeping to work. There is probably a less hacky way but I will post my solution below! – John Pavley Apr 19 '15 at 18:15

2 Answers2

2

NSSpeechSynthesizer.isAnyApplicationSpeaking() waits until the speech is finished.

import Foundation
import AppKit

class Speaker: NSObject , NSSpeechSynthesizerDelegate {
    var synth : NSSpeechSynthesizer!

    func run() {
        self.synth = NSSpeechSynthesizer.init()
        self.synth.delegate = self
        self.synth.startSpeakingString("First word")
        while(NSSpeechSynthesizer.isAnyApplicationSpeaking() == true) {}
    }
}

var speak : Speaker = Speaker.init()
speak.run()
pepe
  • 198
  • 1
  • 1
  • 7
0

Thanks for the comments I discovered I needed a run loop. I found inspiration on GitHub pannous/caffe-speech-recognition and rewrote by code to look like this:

import Foundation
import AppKit

println("Hello, World!")

//var synth:NSSpeechSynthesizer = NSSpeechSynthesizer.init()
//var result = synth.startSpeakingString("dit doh")
//println(result)

@objc class SynthDelegate : NSObject, NSSpeechSynthesizerDelegate {

    func run() {
        var synth = NSSpeechSynthesizer.init()
        synth.delegate = self
        synth.startSpeakingString("dit doh")
    }
}

SynthDelegate().run()
sleep(2);

I feel the sleep(2) function call is a bit of a hack (and not the good kind). I'll need to research how to do it better.

John Pavley
  • 5,366
  • 2
  • 14
  • 16