Как сделать iOS речевым к тексту постоянным
Я провожу первоначальное исследование нового потенциального продукта. Часть этого продукта требует, чтобы Speech-To-Text на iPhone и iPad оставался включенным до тех пор, пока пользователь не отключит его. После того, как я сам его использовал, я заметил, что он либо автоматически отключается через 30 секунд, независимо от того, перестал ли пользователь говорить, либо он отключается после того, как из динамика произойдет определенное количество сомнительных слов. В любом случае, этот продукт требует, чтобы он оставался включенным все время, пока явно не будет указано, что нужно остановиться. Кто-нибудь работал с этим раньше? И да, я пробовал хороший поиск, я не мог найти ничего существенного, и особенно ничего написанного на правильном языке. Спасибо, друзья!
4 ответа
Лучший ответ на это, чтобы Google больше! Попробовав несколько разных ключевых слов, вы наткнетесь на этот документ, найденный здесь. Как видно из прочтения, в разделе распознавания речи они упоминают, что
iOS 10 представляет новый API, который поддерживает непрерывное распознавание речи и помогает создавать приложения, которые могут распознавать речь и транскрибировать ее в текст.
Еще более удивительно, что они даже включают некоторый код для использования при определении нового распознавателя речи, как показано ниже:
let recognizer = SFSpeechRecognizer()
let request = SFSpeechURLRecognitionRequest(url: audioFileURL)
recognizer?.recognitionTask(with: request, resultHandler: { (result, error) in
print (result?.bestTranscription.formattedString)
})
Потрясающие! Так вот и все, легкий поиск. Надеюсь, что это поможет некоторым людям в будущем, так как я уверен, что придет день, когда нам больше не придется использовать наши пальцы, чтобы пользоваться телефоном.
Я нашел здесь учебник, который покажет вашу речь. Но смотрите заметки:
Apple ограничивает распознавание для каждого устройства. Предел не известен, но вы можете связаться с Apple для получения дополнительной информации. Apple ограничивает распознавание для каждого приложения.
Если вы регулярно превышаете лимиты, обязательно свяжитесь с Apple, они, вероятно, могут ее решить.
Распознавание речи использует много энергии и данных.
Распознавание речи длится около минуты за раз.
РЕДАКТИРОВАТЬ
Этот ответ был для iOS 10. Я ожидаю выхода iOS 12 в октябре 2018 года, но Apple по-прежнему говорит:
Запланируйте ограничение продолжительности звука в одну минуту. Распознавание речи может значительно увеличить время автономной работы и использования сети. В iOS 10 продолжительность звукового сопровождения ограничена примерно одной минутой, что аналогично пределу для диктовки, связанной с клавиатурой.
Смотрите: https://developer.apple.com/documentation/speech
Там нет никаких изменений API в Speech
Framework для iOS 11 и 12. Подробно смотрите все изменения API, особенно для iOS 12, Пол Хадсон: API iOS 12 отличается
Так что мой ответ должен быть действительным.
Это поможет вам автоматически запускать запись каждые 40 секунд, даже если вы ничего не говорите. Если вы что-то говорите, а затем на 2 секунды наступает тишина, она прекращается и вызывается функция didfinishtalk.
@objc func startRecording() {
self.fullsTring = ""
audioEngine.reset()
if recognitionTask != nil {
recognitionTask?.cancel()
recognitionTask = nil
}
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(.record)
try audioSession.setMode(.measurement)
try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
try audioSession.setPreferredSampleRate(44100.0)
if audioSession.isInputGainSettable {
let error : NSErrorPointer = nil
let success = try? audioSession.setInputGain(1.0)
guard success != nil else {
print ("audio error")
return
}
if (success != nil) {
print("\(String(describing: error))")
}
}
else {
print("Cannot set input gain")
}
} catch {
print("audioSession properties weren't set because of an error.")
}
recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
let inputNode = audioEngine.inputNode
guard let recognitionRequest = recognitionRequest else {
fatalError("Unable to create an SFSpeechAudioBufferRecognitionRequest object")
}
recognitionRequest.shouldReportPartialResults = true
self.timer4 = Timer.scheduledTimer(timeInterval: TimeInterval(40), target: self, selector: #selector(againStartRec), userInfo: nil, repeats: false)
recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest, resultHandler: { (result, error ) in
var isFinal = false //8
if result != nil {
self.timer.invalidate()
self.timer = Timer.scheduledTimer(timeInterval: TimeInterval(2.0), target: self, selector: #selector(self.didFinishTalk), userInfo: nil, repeats: false)
let bestString = result?.bestTranscription.formattedString
self.fullsTring = bestString!
self.inputContainerView.inputTextField.text = result?.bestTranscription.formattedString
isFinal = result!.isFinal
}
if error == nil{
}
if isFinal {
self.audioEngine.stop()
inputNode.removeTap(onBus: 0)
self.recognitionRequest = nil
self.recognitionTask = nil
isFinal = false
}
if error != nil{
URLCache.shared.removeAllCachedResponses()
self.audioEngine.stop()
inputNode.removeTap(onBus: 0)
guard let task = self.recognitionTask else {
return
}
task.cancel()
task.finish()
}
})
audioEngine.reset()
inputNode.removeTap(onBus: 0)
let recordingFormat = AVAudioFormat(standardFormatWithSampleRate: 44100, channels: 1)
inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer, when) in
self.recognitionRequest?.append(buffer)
}
audioEngine.prepare()
do {
try audioEngine.start()
} catch {
print("audioEngine couldn't start because of an error.")
}
self.hasrecorded = true
}
@objc func againStartRec(){
self.inputContainerView.uploadImageView.setBackgroundImage( #imageLiteral(resourceName: "microphone") , for: .normal)
self.inputContainerView.uploadImageView.alpha = 1.0
self.timer4.invalidate()
timer.invalidate()
self.timer.invalidate()
if ((self.audioEngine.isRunning)){
self.audioEngine.stop()
self.recognitionRequest?.endAudio()
self.recognitionTask?.finish()
}
self.timer2 = Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(startRecording), userInfo: nil, repeats: false)
}
@objc func didFinishTalk(){
if self.fullsTring != ""{
self.timer4.invalidate()
self.timer.invalidate()
self.timer2.invalidate()
if ((self.audioEngine.isRunning)){
self.audioEngine.stop()
guard let task = self.recognitionTask else {
return
}
task.cancel()
task.finish()
}
}
}
///
/// Code lightly adopted by from https://developer.apple.com/documentation/speech/recognizing_speech_in_live_audio?language=swift
///
/// Modifications from original:
/// - Color of text changes every time a new "chunk" of text is transcribed
/// -- This was a feature I added while playing with my nephews. They loved it (2 and 6) (we kept saying rainbow)
/// - I added a bit of logic to scroll to the end of the text once new chunks were added
/// - I formatted the code using swiftformat
///
import Speech
import UIKit
public class ViewController: UIViewController, SFSpeechRecognizerDelegate {
private let speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "en-US"))!
private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?
private var recognitionTask: SFSpeechRecognitionTask?
private let audioEngine = AVAudioEngine()
@IBOutlet var textView: UITextView!
@IBOutlet var recordButton: UIButton!
let colors: [UIColor] = [.red, .orange, .yellow, .green, .blue, .purple]
var colorIndex = 0
override public func viewDidLoad() {
super.viewDidLoad()
textView.textColor = colors[colorIndex]
// Disable the record buttons until authorization has been granted.
recordButton.isEnabled = false
}
override public func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Configure the SFSpeechRecognizer object already
// stored in a local member variable.
speechRecognizer.delegate = self
// Asynchronously make the authorization request.
SFSpeechRecognizer.requestAuthorization { authStatus in
// Divert to the app's main thread so that the UI
// can be updated.
OperationQueue.main.addOperation {
switch authStatus {
case .authorized:
self.recordButton.isEnabled = true
case .denied:
self.recordButton.isEnabled = false
self.recordButton.setTitle("User denied access to speech recognition", for: .disabled)
case .restricted:
self.recordButton.isEnabled = false
self.recordButton.setTitle("Speech recognition restricted on this device", for: .disabled)
case .notDetermined:
self.recordButton.isEnabled = false
self.recordButton.setTitle("Speech recognition not yet authorized", for: .disabled)
default:
self.recordButton.isEnabled = false
}
}
}
}
private func startRecording() throws {
// Cancel the previous task if it's running.
recognitionTask?.cancel()
recognitionTask = nil
// Configure the audio session for the app.
let audioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory(.record, mode: .measurement, options: .duckOthers)
try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
let inputNode = audioEngine.inputNode
// Create and configure the speech recognition request.
recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// The below lines are responsible for keeping the recording active longer
/// than just short bursts. I've had the recording going all day in somewhat
/// rudimentary attempts.
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
if #available(iOS 13, *) {
let supportsOnDeviceRecognition = speechRecognizer.supportsOnDeviceRecognition
if !supportsOnDeviceRecognition {
fatalError("On device transcription not supported on this device. It is safe to remove this error but I wanted to add it as a warning that you'd actually see.")
}
recognitionRequest!.requiresOnDeviceRecognition = /* only appears to work on device; not simulator */ supportsOnDeviceRecognition
}
guard let recognitionRequest = recognitionRequest else { fatalError("Unable to create a SFSpeechAudioBufferRecognitionRequest object") }
recognitionRequest.shouldReportPartialResults = true
// Create a recognition task for the speech recognition session.
// Keep a reference to the task so that it can be canceled.
recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest) { result, error in
var isFinal = false
if let result = result {
// Update the text view with the results.
self.colorIndex = (self.colorIndex + 1) % self.colors.count
self.textView.text = result.bestTranscription.formattedString
self.textView.textColor = self.colors[self.colorIndex]
self.textView.scrollRangeToVisible(NSMakeRange(result.bestTranscription.formattedString.count - 1, 0))
isFinal = result.isFinal
print("Text \(result.bestTranscription.formattedString)")
}
if error != nil || isFinal {
// Stop recognizing speech if there is a problem.
self.audioEngine.stop()
inputNode.removeTap(onBus: 0)
self.recognitionRequest = nil
self.recognitionTask = nil
self.recordButton.isEnabled = true
self.recordButton.setTitle("Start Recording", for: [])
}
}
// Configure the microphone input.
let recordingFormat = inputNode.outputFormat(forBus: 0)
inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer: AVAudioPCMBuffer, _: AVAudioTime) in
self.recognitionRequest?.append(buffer)
}
audioEngine.prepare()
try audioEngine.start()
// Let the user know to start talking.
textView.text = "(Go ahead, I'm listening)"
}
// MARK: SFSpeechRecognizerDelegate
public func speechRecognizer(_: SFSpeechRecognizer, availabilityDidChange available: Bool) {
if available {
recordButton.isEnabled = true
recordButton.setTitle("Start Recording", for: [])
} else {
recordButton.isEnabled = false
recordButton.setTitle("Recognition Not Available", for: .disabled)
}
}
// MARK: Interface Builder actions
@IBAction func recordButtonTapped() {
if audioEngine.isRunning {
audioEngine.stop()
recognitionRequest?.endAudio()
recordButton.isEnabled = false
recordButton.setTitle("Stopping", for: .disabled)
} else {
do {
try startRecording()
recordButton.setTitle("Stop Recording", for: [])
} catch {
recordButton.setTitle("Recording Not Available", for: [])
}
}
}
}
Приведенный выше фрагмент кода при запуске на физическом устройстве будет непрерывно («настойчиво») расшифровывать звук с использованием Apple Speech Framework.
Волшебная линия здесь
request.requiresOnDeviceRecognition = ...
Если
request.requiresOnDeviceRecognition
верно и
SFSpeechRecognizer#supportsOnDeviceRecognition
является
true
, то аудио будет непрерывно транскрибироваться до тех пор, пока не разрядится батарея, пользователь не отменит транскрипцию или не произойдет какая-либо другая ошибка/условие завершения. По крайней мере, это верно в моих испытаниях.
Документы:
https://developer.apple.com/documentation/speech/recognizing_speech_in_live_audio
Заметки:
Первоначально я пытался отредактировать этот ответ [0], но хотел добавить так много деталей, что почувствовал, что он полностью захватил исходный ответчик. В идеале я буду поддерживать свой собственный ответ: подход, который переводит его в SwiftUI, а также в компонуемую архитектуру (принимая их пример [1]) в качестве канонического источника быстрого старта для транскрипции голоса на платформах Apple.
0: /questions/33639372/kak-sdelat-ios-rechevyim-k-tekstu-postoyannyim/33639378#33639378