Multithreading NSSpeechSynthesizer保存到URL
我正在创建一个MacOS应用程序,它从制表符分隔的文本文件生成对话框文件。文本文件已格式化(输出文件名)[tab](要与该文件对话的文本)。我让程序在主线程中完美运行,但这当然会阻塞UI。当我将应用程序的内容移动到用户启动的线程时,我取消了对UI的阻止,但现在它偶尔会随机地从说话者那里说出一行,而不是对文件。文件仍在创建中,但持续时间为零。如果脚本包含1000行,它可能会完美地完成整个过程,或者它可能会“说出”其中的10行或更多行。几乎每次都是不同的台词 这是与文件对话的代码:Multithreading NSSpeechSynthesizer保存到URL,multithreading,swift,macos,grand-central-dispatch,text-to-speech,Multithreading,Swift,Macos,Grand Central Dispatch,Text To Speech,我正在创建一个MacOS应用程序,它从制表符分隔的文本文件生成对话框文件。文本文件已格式化(输出文件名)[tab](要与该文件对话的文本)。我让程序在主线程中完美运行,但这当然会阻塞UI。当我将应用程序的内容移动到用户启动的线程时,我取消了对UI的阻止,但现在它偶尔会随机地从说话者那里说出一行,而不是对文件。文件仍在创建中,但持续时间为零。如果脚本包含1000行,它可能会完美地完成整个过程,或者它可能会“说出”其中的10行或更多行。几乎每次都是不同的台词 这是与文件对话的代码: // Set U
// Set URL to save file to disk
if let speechFileURL = NSURL(string: speechFilePath) {
speechSynth.startSpeakingString(spokenText, toURL: speechFileURL) // Speak to file
}
else {
dialogAlert("Invalid File: ", text: speechFileName)
}
while(NSSpeechSynthesizer.isAnyApplicationSpeaking() == true) {
if self.gUserClickedStop {
speechSynth.stopSpeaking()
self.gIsSpeaking = false
break
}
}
这是对解析脚本并生成语音文件的函数的调用:
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) { [unowned self] in
self.parseTheScript()
}
如上所述,调用parseTheScript()而不在Grand Central Dispatch调用中对其进行包装可以使其完美工作,除了阻塞问题
我不熟悉Swift编程和多线程编程的概念,所以我肯定我做错了什么,但我很难解决这个问题
提前感谢您的帮助
编辑以添加
这是解析文本文件并生成对话框文件的整个函数:
// This opens the text file and generates the spoken audio files
func parseTheScript() {
let filemgr = NSFileManager.defaultManager()
print("Chosen path: \(scriptFileTextField.stringValue)") // debug
if filemgr.fileExistsAtPath(scriptFileTextField.stringValue) {
print("File exists")
do {
outputFilename.stringValue = ""
scriptToSpeak.stringValue = ""
outputFilename.hidden = false
scriptToSpeak.hidden = false
progressBar.minValue = 0
progressBar.doubleValue = 0.0
progressBar.hidden = false
filenameLabel.hidden = false
let text = try String(contentsOfFile: scriptFileTextField.stringValue, encoding: NSUTF8StringEncoding)
let lines: [String] = text.componentsSeparatedByString("\n")
var progressEndValue = Double(lines.count)
progressBar.maxValue = progressEndValue
let speechSynth = NSSpeechSynthesizer.init()
let theVoice = gVoiceList[voiceListPopup.indexOfSelectedItem - 1] // Not sure why it's off by one
speechSynth.setVoice(theVoice)
for line in lines {
let thisLine: [String] = line.componentsSeparatedByString("\t") // Split the line at the tab character
if (thisLine.count == 2) { // Did the line split into two parts?
let speechFileName = thisLine[0]
let spokenText = thisLine[1]
outputFilename.stringValue = speechFileName
scriptToSpeak.stringValue = spokenText
let speechFilePath = destinationFolderTextField.stringValue + speechFileName + ".aif" // Build the path string for the output speech file
print("Filename: \(speechFilePath)")
print("SpokenText: \(spokenText)")
if(gSpeakToFile) {
// Set URL to save file to disk
if let speechFileURL = NSURL(string: speechFilePath) {
speechSynth.startSpeakingString(spokenText, toURL: speechFileURL) // Speak to file
}
else {
dialogAlert("Invalid File: ", text: speechFileName)
}
}
else {
speechSynth.startSpeakingString(spokenText) // Speak to audio output
}
while(NSSpeechSynthesizer.isAnyApplicationSpeaking() == true) {
if self.gUserClickedStop {
speechSynth.stopSpeaking()
self.gIsSpeaking = false
break
}
}
progressBar.incrementBy(1.0)
}
if gUserClickedStop {
gUserClickedStop = false
gIsSpeaking = false
progressEndValue = 0
generateButton.title = "Start"
break
}
}
gIsSpeaking = false
progressBar.doubleValue = progressEndValue
generateButton.title = "Start"
filenameLabel.hidden = true
outputFilename.stringValue = ""
scriptToSpeak.stringValue = ""
} catch let error as NSError {
dialogAlert("Error:", text: String(error.localizedDescription))
print("Error: \(error)")
}
} else {
print("File does not exist")
}
}
我也不认为它在主线程上运行得“完美”。您需要正确地实现委托方法,例如
speechSynthesizer:didFinishSpeaking:
,以便在开始峰值字符串
完成时收到通知。您可以先查看一个示例,了解如何使用NSSpeechSyntheziser
。无可否认,我一直将委托方法视为可选方法,因此我将进一步深入研究。我所说的“完美”,是指应用程序产生的结果正是我想要的结果。我认为您没有显示足够的代码。第一段代码何时执行?self.parseTheScript()的代码在哪里?我已经添加了整个parseTheScript函数。@MakerOfSound-你真的得到了NSSpeechSynthsizer的aiff(或任何东西)输出吗?我已经在我的一个应用程序中使用这个类有一段时间了,尽管我以前从未使用过toURL选项。当我调用它时,它返回的结果是一切都很好,实际上一切似乎都很好,只是没有生成任何文件。我认为它在主线程上运行也不是“完美的”。您需要正确地实现委托方法,例如speechSynthesizer:didFinishSpeaking:
,以便在开始峰值字符串
完成时收到通知。您可以先查看一个示例,了解如何使用NSSpeechSyntheziser
。无可否认,我一直将委托方法视为可选方法,因此我将进一步深入研究。我所说的“完美”,是指应用程序产生的结果正是我想要的结果。我认为您没有显示足够的代码。第一段代码何时执行?self.parseTheScript()的代码在哪里?我已经添加了整个parseTheScript函数。@MakerOfSound-你真的得到了NSSpeechSynthsizer的aiff(或任何东西)输出吗?我已经在我的一个应用程序中使用这个类有一段时间了,尽管我以前从未使用过toURL选项。当我调用它时,它返回的结果是一切都很好,实际上似乎一切都很好,只是没有生成任何文件。