SFSpeechURLRecognitionRequest не выводит результаты распознавания речи, пока не завершится вызов потока

#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) {}
 

до конца непосредственно перед последней печатью (результаты начинают появляться немедленно, а окончательная печать печатается сразу после окончательных результатов).