Hi all,
as I’ve said in the title, I was carried away by the challenge and I kept building features over features. The VocalTextEdit now has the opportunity to read in 2 languages, increase the size of text from the menu, undo and redo functionality, a check button to allow the voice to tell you how many words are in the text and a pause/resume button.
There is something that keep me banging my head on the wall:
When I choose English voice Alex the pause/resume feature works like a charm but when I use the Italian Alice voice it seems that the didFinishSpeaking got called anyway. I’ve tried swap the code between the 2 voices with no luck. I’m baffled. I’m sure that is something right in front of my eyes but I can’t quite figure out what it is and I hope that someone of you, more experienced coders, will point me towards the solution. To be more precise if I use the Italian voices(female and male) and I pause during the reading after a second of pause the didFinishSpeaking got called and it stops
//
// ViewController.swift
// VocalTextEdit
//
// Created by Stefano Ballirano on 20/03/21.
//
import Cocoa
var numberOfWords = 0
class ViewController: NSViewController, NSSpeechSynthesizerDelegate, NSTextDelegate, NSTextViewDelegate {
var speechSynthesizers = NSSpeechSynthesizer()
@IBOutlet weak var textView: NSTextView!
@IBOutlet weak var voiceChoice: NSPopUpButton!
@IBOutlet weak var speakButton: NSButton!
@IBOutlet weak var stopButton: NSButton!
@IBOutlet weak var progressBar: NSProgressIndicator!
@IBOutlet weak var enableSpeakInfo: NSButton!
@IBOutlet weak var pauseButton: NSButton!
var content: String {
get {
return textView.string
}
set {
textView.string = newValue
}
}
override func viewDidLoad() {
voiceChoice.removeAllItems()
voiceChoice.addItems(withTitles: ["Inglese", "Italiano"])
content = textView.string
speakButton.isEnabled = true
stopButton.isEnabled = false
progressBar.doubleValue = 0.0
progressBar.isHidden = true
progressBar.isIndeterminate = false
progressBar.controlSize = .large
progressBar.controlTint = .defaultControlTint
speechSynthesizers.delegate = self
self.textView.delegate = self
enableSpeakInfo.state = .off
}
func evaluateNumberOfWords(textView: NSTextView) -> Int {
return textView.string.components(separatedBy: " ").count + 1
}
func setSpeakingVoice(speech: NSSpeechSynthesizer, lingua: String) {
if lingua == "Inglese" {
speech.setVoice(NSSpeechSynthesizer.VoiceName.init(rawValue: "com.apple.speech.synthesis.voice.Alex"))
} else if lingua == "Italiano" {
speech.setVoice(NSSpeechSynthesizer.VoiceName.init(rawValue: "com.apple.speech.synthesis.voice.alice.premium"))
}
}
@IBAction func speakButtonClicked(sender: NSButton) {
progressBar.isHidden = false
progressBar.doubleValue = 0.0
progressBar.isBezeled = true
speakButton.isEnabled = false
stopButton.isEnabled = true
numberOfWords = evaluateNumberOfWords(textView: textView)
if !textView.string.isEmpty {
if voiceChoice.titleOfSelectedItem == "Inglese" {
setSpeakingVoice(speech: speechSynthesizers, lingua: "Inglese")
if enableSpeakInfo.state == .on {
speechSynthesizers.startSpeaking("The text is \(numberOfWords) words long. " + textView.string )
} else {
speechSynthesizers.startSpeaking(textView.string)
}
} else if voiceChoice.titleOfSelectedItem == "Italiano"{
setSpeakingVoice(speech: speechSynthesizers, lingua: "Italiano")
if enableSpeakInfo.state == .on {
speechSynthesizers.startSpeaking("Il testo è lungo \(numberOfWords) parole. " + textView.string )
} else {
speechSynthesizers.startSpeaking(textView.string)
}
}
} else {
if voiceChoice.titleOfSelectedItem == "Inglese" {
setSpeakingVoice(speech: speechSynthesizers, lingua: "Inglese")
speechSynthesizers.startSpeaking("The document is empty")
} else {
setSpeakingVoice(speech: speechSynthesizers, lingua: "Italiano")
speechSynthesizers.startSpeaking("Il documento è vuoto")
}
}
}
@IBAction func stopButtonClicked(sender: NSButton) {
speechSynthesizers.stopSpeaking()
}
func speechSynthesizer(_ sender: NSSpeechSynthesizer, didFinishSpeaking finishedSpeaking: Bool) {
speakButton.isEnabled = true
stopButton.isEnabled = false
progressBar.doubleValue = 100.0
progressBar.isHidden = true
}
func speechSynthesizer(_ sender: NSSpeechSynthesizer, willSpeakWord characterRange: NSRange, of string: String) {
let totalCharacterNumber = Double(string.count)
let spacesCount = string.components(separatedBy: " ")
let charactersToSay = Double(characterRange.length)
progressBar.increment(by: ( (100.0 / totalCharacterNumber) * charactersToSay) + ((Double(spacesCount.count - 1) * (100.0 / totalCharacterNumber) / (Double(spacesCount.count)) ) ) )
}
func textDidChange(_ notification: Notification) {
numberOfWords = evaluateNumberOfWords(textView: textView)
}
@IBAction func increaseFontSize(sender: NSMenuItem) {
let actualFontSize = textView.font?.pointSize
let font = NSFont(name: "Helvetica", size: actualFontSize! + 1)
textView.font = font
}
@IBAction func decreaseFontSize(sender: NSMenuItem) {
let actualFontSize = textView.font?.pointSize
let font = NSFont(name: "Helvetica", size: actualFontSize! - 1)
textView.font = font
}
@IBAction func buttonActionUndo(_ sender: Any) {
undoManager?.undo()
}
@IBAction func buttonActionRedo(_ sender: Any) {
undoManager?.redo()
}
@IBAction func pauseSpeaking(_ sender: NSButton) {
if sender.title == "PAUSE" {
sender.title = "RESUME"
speechSynthesizers.pauseSpeaking(at: .wordBoundary)
} else {
sender.title = "PAUSE"
speechSynthesizers.continueSpeaking()
}
}
}