How to Convert Text to Speech for Osx in Swift Playground

How to convert text to speech for OSX in Swift playground

NSSpeechSynthesizer's .startSpeaking needs to execute in a background task, but by default this is not possible in a Playground.

You can enable it by importing PlaygroundSupport and setting asynchronous mode, like this:

import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

Swift playgrounds ios 10 text to speech command code

utterance: AVSpeechUtterance is just an editor placeholder that tells you what you should put there:

synthesizer.speak(utterance: AVSpeechUtterance)

You need to call it passing it the utterance object you created:

synthesizer.speak(utterance)

To get it to speak, you need a few more lines. Here is the complete code:

import AVFoundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let synthesizer = AVSpeechSynthesizer()
let utterance = AVSpeechUtterance(string: "Say Hello")

utterance.rate = 0.5

synthesizer.speak(utterance)

speech to text macOS swift playground

It seems like the Speech library is only available on iOS, so you're out of luck with SFSpeechRecognizer. NSSpeechRecognizer could be an alternative but requires you to give a list of words for it to recognize from (rather than recognizing from any arbitrary word).

Where are the reference strings for NSVoiceLocaleIdentifier?

Stitching together a few things--first credit where credit is due, thanks to Eric Aya for explaining how to get an Xcode playground to do speech synthesis, George Warner for sample code printing out the available voices and locations, and of course Matt in the comment above who oriented me to a working path. Unless I have this very wrong, it would appear that various voices are oriented around specific languages. So I believe you set the voice for the location.

Here's a setup for an Xcode playground:

import Cocoa
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

Here's how to get a voice speaking in Spanish:

let synth = NSSpeechSynthesizer()
synth.setVoice( "com.apple.speech.synthesis.voice.paulina.premium" )
synth.startSpeaking( "Hola Mundo mañana" )

How did I know that com.apple.speech.synthesis.voice.paulina.premium spoke in Spanish?

for voiceIdentifierString: String in NSSpeechSynthesizer.availableVoices() {
var voiceLocaleIdentifier = ( NSSpeechSynthesizer.attributes( forVoice: voiceIdentifierString )[ NSVoiceLocaleIdentifier ] as! String )
print( "\(voiceIdentifierString) speaks \(voiceLocaleIdentifier)" )
}

If I'm wrong in my assumptions, please correct me. I'm posting this for anyone who may encounter a similar need.

iOS Text To Speech Api

Since iOS 7 you have a new TTS Api.

In Objective C

AVSpeechSynthesizer *synthesizer = [[AVSpeechSynthesizer alloc]init];
AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString:@"Some text"];
[utterance setRate:0.2f];
[synthesizer speakUtterance:utterance];

In Swift

let synthesizer = AVSpeechSynthesizer()
let utterance = AVSpeechUtterance(string: "Some text")
utterance.rate = 0.2

You can also change the voice like this :

utterance.voice = AVSpeechSynthesisVoice(language: "fr-FR")

And then speek

  • In Swift 2
    synthesizer.speakUtterance(utterance)

  • In Swift 3
    synthesizer.speak(utterance)

Don't forget to import AVFoundation

Helpful methods

You can Stop or Pause all speech using these two methods :

- (BOOL)pauseSpeakingAtBoundary:(AVSpeechBoundary)boundary;
- (BOOL)stopSpeakingAtBoundary:(AVSpeechBoundary)boundary;

The AVSpeechBoundary indicates if the speech should pause or stop immediately (AVSpeechBoundaryImmediate) or it should pause or stop after the word currently being spoken (AVSpeechBoundaryWord).

Check the AVSpeechSynthesizer Doc

NSSpeechSynthesizer get Siri voices on macOS

Siri voice

I'm not aware of any trick to make the Siri voice available and I'm afraid it's impossible to make it working via public API.

  • Both NSSpeechSynthesizer and AVSpeechSynthesizer do utilize SpeechSynthesis.framework (included in the ApplicationServices.framework).
  • Quickly checked it and the function BuildDefaultVoiceList(bool) checks bundle identifier (via PermitAllVoices which compares it with com.apple.VoiceOverUtility, com.apple.VoiceOver, com.apple.VoiceOverUtilityCacheBuilder).
  • There're other checks probably.

All the voices are stored in the /System/Library/Speech/Voices folder. Tried to copy the voice, change Info.plist values to make it look like another voice, but still not available/working.

How far do you want to go? Do you want to disable SIP, modify framework, preload your stuff, ... Is it worth it?

AVSpeech​Synthesizer on macOS

It works, but it's buggy and you have to kill the service from time to time.

List of available voices

AVSpeechSynthesisVoice.speechVoices().forEach { voice in
print("Name: \(voice.name) Language: \(voice.language) Identifier: \(voice.identifier)")
}

Speak

let utterance = AVSpeechUtterance(string: "Ahoj, jak se máš?")
utterance.voice = AVSpeechSynthesisVoice(identifier: "com.apple.speech.synthesis.voice.zuzana.premium")

let synthesizer = AVSpeechSynthesizer()
synthesizer.speak(utterance)

Doesn't speak?

All the following methods (delegate) are called when you call speak(utterance) and it works as it should:

  • speechSynthesizer(_:didStart:)
  • speechSynthesizer(_:willSpeakRangeOfSpeechString:utterance:)
  • speechSynthesizer(_:didFinish:)

Something's wrong if you get just the speechSynthesizer(_:didCancel:) method call and you have to kill the speechsynthesisd process:

kill `pgrep speechsynthesisd`

Then try again, it fixes the issue for me.

Additional voices installation

  • System Preferences - Accessibility - Speech
  • System Voice - click & scroll down & select Customize...
  • Select additional voice to install

Xcode: Any way to refresh/re-run the playground?

Try Editor > Execute Playground from Xcode menu

Sample Image

I don't know what Reset Playground is, by the way.

Converting a Vision VNTextObservation to a String

Apple finally updated Vision to do OCR. Open a playground and dump a couple of test images in the Resources folder. In my case, I called them "demoDocument.jpg" and "demoLicensePlate.jpg".

The new class is called VNRecognizeTextRequest. Dump this in a playground and give it a whirl:

import Vision

enum DemoImage: String {
case document = "demoDocument"
case licensePlate = "demoLicensePlate"
}

class OCRReader {
func performOCR(on url: URL?, recognitionLevel: VNRequestTextRecognitionLevel) {
guard let url = url else { return }
let requestHandler = VNImageRequestHandler(url: url, options: [:])

let request = VNRecognizeTextRequest { (request, error) in
if let error = error {
print(error)
return
}

guard let observations = request.results as? [VNRecognizedTextObservation] else { return }

for currentObservation in observations {
let topCandidate = currentObservation.topCandidates(1)
if let recognizedText = topCandidate.first {
print(recognizedText.string)
}
}
}
request.recognitionLevel = recognitionLevel

try? requestHandler.perform([request])
}
}

func url(for image: DemoImage) -> URL? {
return Bundle.main.url(forResource: image.rawValue, withExtension: "jpg")
}

let ocrReader = OCRReader()
ocrReader.performOCR(on: url(for: .document), recognitionLevel: .fast)

There's an in-depth discussion of this from WWDC19



Related Topics



Leave a reply



Submit