#swift #macos #speech-recognition
#swift #macos #Распознавание речи
Вопрос:
Я пытаюсь использовать SFSpeechURLRecognitionRequest
для транскрибирования аудиофайлов в терминальном приложении. Пока я работаю в предварительной форме, я сталкиваюсь со странной проблемой. Похоже, что результаты распознавания голоса (как частичные, так и полные) выводятся только после завершения основного потока в моих тестовых приложениях. Обратите внимание, что я новичок в Swift, поэтому, возможно, я упускаю что-то очевидное.
Ниже у меня есть полное приложение Xcode Playground, которое демонстрирует проблему. Вывод с игровой площадки записывается Playground Execution Complete
, а затем я начинаю получать частичные выходные данные, за которыми следует окончательный вывод. Обратите внимание, что если я добавлю sleep(5)
перед print
ним, он подождет 5 секунд, а затем выведет print
, и только тогда после завершения основного потока начнется обработка текста. Я видел подобное поведение в тестовом приложении с графическим интерфейсом, где он начинает обрабатывать текст только после завершения вызова метода, запускающего запрос.
Я неоднократно пытался проверять состояние возвращаемой задачи, переходя в спящий режим между каждой проверкой, но безуспешно.
Я также попытался вызвать задачу распознавания внутри DispatchQueue, которая, похоже, успешно выполняется в фоновом режиме в зависимости от загрузки процессора, но частичные и окончательные распечатки никогда не появляются до завершения работы приложения, после чего консоль заполняется частичными данными, за которыми следует Final .
Кто-нибудь знает способ начать обработку распознавания речи без завершения потока приложения? В идеале я хотел бы иметь возможность запускать его и повторно переходить в режим сна на короткие периоды, проверяя, выполнена ли задача распознавания между каждым.
Отредактировано ниже, чтобы соответствовать версии непосредственно перед поиском решения.
import Speech
var complete = false
SFSpeechRecognizer.requestAuthorization {
authStatus in
DispatchQueue.main.async {
if authStatus == .authorized {
print("Good to go!")
} else {
print("Transcription permission was declined.")
exit(1)
}
}
}
guard let myRecognizer = SFSpeechRecognizer() else {
print("Recognizer not supported for current locale!")
exit(1)
}
if !myRecognizer.isAvailable {
// The recognizer is not available right now
print("Recognizer not available right now")
exit(1)
}
if !myRecognizer.supportsOnDeviceRecognition {
print("On device recognition not possible!")
exit(1)
}
let path_to_wav = NSURL.fileURL(withPath: "/tmp/harvard.wav", isDirectory: false)
let request = SFSpeechURLRecognitionRequest(url: path_to_wav)
request.requiresOnDeviceRecognition = true
print("About to create recognition task...")
myRecognizer.recognitionTask(with: request) {
(result, error) in
guard let result = result else {
// Recognition failed, so check error for details and handle it
print("Recognition failed!!!")
print(error!)
exit(1)
}
if result.isFinal {
print("Final: (result.bestTranscription.formattedString)")
complete = true
} else {
print("Partial: (result.bestTranscription.formattedString)")
}
}
print("Playground execution complete.")
Комментарии:
1. Как вы поняли, обработчик завершения выполняется асинхронно и будет выполняться намного медленнее, чем ваш основной поток. Вы можете добавить
PlaygroundPage.current.needsIndefiniteExecution = true
инструкцию на свою игровую площадку, чтобы она продолжала выполняться, что позволит вам получать отложенные асинхронные ответы.2. и вы можете вызвать
PlaygroundPage.current.finishExecution()
, когда это будет окончательно сделано, возможно, из обработчика завершения?3. Я понимаю, что он асинхронный, моя основная проблема заключается в том, что результаты не появляются до окончания основного потока. Я могу спать >> дольше, чем требуется для обработки, и все же все распечатанные выходные данные вызова не отображаются до тех пор, пока не завершится основной поток. Пример: для harvard.wav, который я использую для вышеупомянутого теста, обработка занимает ~ 10 секунд. Если я добавляю режим ожидания на 20 секунд, я вижу всплеск загрузки ЦП в течение первых 10 секунд, а затем загрузка ЦП падает, но никогда не получаю никакого вывода на консоль еще 10 секунд, пока выполнение не остановится.
4. Если вы спите в основном потоке, я почти уверен, что он заблокирует основной поток, поэтому он не будет доступен для любого вывода, пока не закончится режим ожидания. В этот момент он будет записывать на консоль, как и ожидалось. Я не совсем понимаю, на какое поведение вы надеетесь, но если вы хотите что-то сделать с выводом, ypu придется вызвать это действие из обработчика завершения (и на игровой площадке установить неопределенное выполнение, как указано выше)
Ответ №1:
Я понял это! sleep
фактически не позволяет выполнять фоновые задачи. Вместо этого, добавив следующее:
let runLoop = RunLoop.current
let distantFuture = NSDate.distantFuture as NSDate
while complete == false amp;amp; runLoop.run(mode: RunLoop.Mode.default, before: distantFuture as Date) {}
до конца непосредственно перед последней печатью (результаты начинают появляться немедленно, а окончательная печать печатается сразу после окончательных результатов).