Остановить запись после тишины X секунд

Я реализую следующее руководство: Speech To Text

Я записываю звук с помощью AVAudioEngine и использую SFSpeechRecognizer для преобразования речи в текст. Здесь учебник переводит речь в текст с помощью кнопки, которая запускает и останавливает запись.

Я начинаю запись на viewDidAppear, когда приложение получает разрешение. Но я не могу найти способ остановить запись после нескольких секунд тишины. Вот мой код:

import UIKit
import Speech

public class ViewController: UIViewController, SFSpeechRecognizerDelegate {
// MARK: Properties

private let speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "en-US"))!

private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?

private var recognitionTask: SFSpeechRecognitionTask?

private let audioEngine = AVAudioEngine()

@IBOutlet var textView : UILabel!

@IBOutlet var recordButton : UIButton!


public override func viewDidLoad() {
    super.viewDidLoad()

    recordButton.isEnabled = false
}

override public func viewDidAppear(_ animated: Bool) {
    speechRecognizer.delegate = self

    SFSpeechRecognizer.requestAuthorization { authStatus in
        /*
         The callback may not be called on the main thread. Add an
         operation to the main queue to update the record button's state.
         */
        OperationQueue.main.addOperation {
            switch authStatus {
            case .authorized:
                self.recordButton.isEnabled = true
                try! self.startRecording()
                self.recordButton.setTitle("Stop recording", for: [])


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

@IBAction func recordButtonTapped() {
    if audioEngine.isRunning {
        audioEngine.stop()
        recognitionRequest?.endAudio()
        recordButton.isEnabled = false
        recordButton.setTitle("Start Recording", for: [])
    } else {
        try! startRecording()
        recordButton.setTitle("Stop recording", for: [])
    }
}

private func startRecording() throws {

    // Cancel the previous task if it's running.
    if let recognitionTask = recognitionTask {
        recognitionTask.cancel()
        self.recognitionTask = nil
    }

    let audioSession = AVAudioSession.sharedInstance()
    try audioSession.setCategory(AVAudioSessionCategoryRecord)
    try audioSession.setMode(AVAudioSessionModeMeasurement)
    try audioSession.setActive(true, with: .notifyOthersOnDeactivation)

    recognitionRequest = SFSpeechAudioBufferRecognitionRequest()

    guard let inputNode = audioEngine.inputNode else { 
        fatalError("Audio engine has no input node") 
    }
    guard let recognitionRequest = recognitionRequest else { 
        fatalError("Unable to created a SFSpeechAudioBufferRecognitionRequest object") 
    }

    // Configure request so that results are returned before audio 
    // recording is finished
    recognitionRequest.shouldReportPartialResults = true

    // A recognition task represents a speech recognition session.
    // We keep a reference to the task so that it can be cancelled.
    recognitionTask = speechRecognizer.recognitionTask(
        with: recognitionRequest
    ) { result, error in
        var isFinal = false

        if let result = result {
            self.textView.text = result.bestTranscription.formattedString
            isFinal = result.isFinal
        }

        if error != nil || isFinal {
            self.audioEngine.stop()
            inputNode.removeTap(onBus: 0)

            self.recognitionRequest = nil
            self.recognitionTask = nil

            self.recordButton.isEnabled = true
        }
    }

    let recordingFormat = inputNode.outputFormat(forBus: 0)
    inputNode.installTap(
        onBus: 0, 
        bufferSize: 1024,
        format: recordingFormat
    ) { (buffer: AVAudioPCMBuffer, when: AVAudioTime) in
        self.recognitionRequest?.append(buffer)
    }

    audioEngine.prepare()

    try audioEngine.start()

    textView.text = "(Go ahead, I'm listening)"
}

// MARK: SFSpeechRecognizerDelegate

public func speechRecognizer(
    _ 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


}

Приложение работает отлично. Но я хочу реализовать остановку audioEngine через X секунд тишины.


person Rocky Balboa    schedule 01.02.2017    source источник


Ответы (1)


Возможно, использовать таймер с заданным интервалом и отключать его каждый раз, когда вы получаете голосовой ввод.

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

Пример :

class text {
var timer:Timer?
   func startRecording() { createTimer(4) }

   func whileRecording() { createTimer(1) }

   func createTimer(_ interval:Double) {
       timer?.invalidate()
       timer =   Timer.scheduledTimer(withTimeInterval: interval, repeats: false) { (_) in
           if self.audioEngine.isRunning {
               self.stopRecording()
           }
       }
   }

}
person Ram Y    schedule 08.02.2017
comment
Это не ответ, потому что всякий раз, когда я делаю таймер недействительным, его нельзя использовать снова. Предположим, я запустил таймер, чтобы ждать 5 секунд. Но когда пользователь что-то говорит, я хочу, чтобы этот таймер снова запустился и подождал 5 секунд. Поэтому, если я аннулирую предыдущий таймер, его нельзя будет использовать снова. И если пользователь говорит 100 раз (просто воображение худшего случая), мне нужно создать таймер 100 раз, аннулировать 99 из них и каждый раз ждать следующего. Так что это не практическое решение. - person Rocky Balboa; 20.02.2017
comment
@RockBalbao см. Мой обновленный ответ - это неправда. Я использую только один таймер и постоянно аннулирую этот таймер - person Ram Y; 20.02.2017
comment
@ Рам, ты можешь сказать мне, где мне вызвать таймер? - person chetan panchal; 08.03.2018
comment
@chetanpanchal - пример. если пользователь нажимает на запись в первый раз (например, обратный вызов requestRecordPermission), вы создаете таймер. в каждом speechRecognizer.recognitionTask вы звоните whileRecording - person Ram Y; 08.03.2018