From ead9075c8600b2831dbd709b9e1c7350c0a9659d Mon Sep 17 00:00:00 2001 From: Cay Zhang Date: Wed, 26 Aug 2020 19:32:10 +0800 Subject: [PATCH] handled the errors happened during startRecording by passing it to resultSubject --- README.md | 2 + Sources/SwiftSpeech/Session.swift | 4 +- Sources/SwiftSpeech/SpeechRecognizer.swift | 99 +++++++++++----------- Sources/SwiftSpeech/ViewModifiers.swift | 4 +- 4 files changed, 57 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index dcf1286..0bc6f78 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,8 @@ In `handleResult`, the first closure parameter is a `SwiftSpeech.Session`, which The second is a [`SFSpeechRecognitionResult`](https://developer.apple.com/documentation/speech/sfspeechrecognitionresult), which contains rich information about the recognition. Not only the recognized text (`result.bestTranscription.formattedString`), but also interesting stuff like **speaking rate** and **pitch**! +In `handleError`, you will handle the errors produced in the recognition process and also during the initialization of the recording session (such as a microphone activation failure). + ```swift // 2 .onStartRecording(appendAction: (SwiftSpeech.Session) -> Void) diff --git a/Sources/SwiftSpeech/Session.swift b/Sources/SwiftSpeech/Session.swift index a91970d..ceabce1 100644 --- a/Sources/SwiftSpeech/Session.swift +++ b/Sources/SwiftSpeech/Session.swift @@ -41,9 +41,9 @@ extension SwiftSpeech { 2. An AVAudioSession error occurred 3. The driver failed to start the hardware */ - public func startRecording() throws { + public func startRecording() { guard let recognizer = SpeechRecognizer.recognizer(withID: id) else { return } - try recognizer.startRecording() + recognizer.startRecording() } public func stopRecording() { diff --git a/Sources/SwiftSpeech/SpeechRecognizer.swift b/Sources/SwiftSpeech/SpeechRecognizer.swift index 17af6c1..b2e73ce 100644 --- a/Sources/SwiftSpeech/SpeechRecognizer.swift +++ b/Sources/SwiftSpeech/SpeechRecognizer.swift @@ -42,60 +42,63 @@ public class SpeechRecognizer { .eraseToAnyPublisher() } - public func startRecording() throws { - - // Cancel the previous task if it's running. - recognitionTask?.cancel() - self.recognitionTask = nil - - // Configure the audio session for the app if it's on iOS/Mac Catalyst. - #if canImport(UIKit) - let audioSession = AVAudioSession.sharedInstance() - try audioSession.setCategory(.record, mode: .measurement, options: .duckOthers) - try audioSession.setActive(true, options: .notifyOthersOnDeactivation) - #endif - - let inputNode = audioEngine.inputNode + public func startRecording() { + do { + // Cancel the previous task if it's running. + recognitionTask?.cancel() + self.recognitionTask = nil + + // Configure the audio session for the app if it's on iOS/Mac Catalyst. + #if canImport(UIKit) + let audioSession = AVAudioSession.sharedInstance() + try audioSession.setCategory(.record, mode: .measurement, options: .duckOthers) + try audioSession.setActive(true, options: .notifyOthersOnDeactivation) + #endif + + let inputNode = audioEngine.inputNode - // Create and configure the speech recognition request. - recognitionRequest = SFSpeechAudioBufferRecognitionRequest() - guard let recognitionRequest = recognitionRequest else { fatalError("Unable to create a SFSpeechAudioBufferRecognitionRequest object") } - - // Use `sessionConfiguration` to configure the recognition request - recognitionRequest.shouldReportPartialResults = sessionConfiguration.shouldReportPartialResults - recognitionRequest.requiresOnDeviceRecognition = sessionConfiguration.requiresOnDeviceRecognition - recognitionRequest.taskHint = sessionConfiguration.taskHint - recognitionRequest.contextualStrings = sessionConfiguration.contextualStrings - recognitionRequest.interactionIdentifier = sessionConfiguration.interactionIdentifier - - // Create a recognition task for the speech recognition session. - // Keep a reference to the task so that it can be cancelled. - recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest) { [weak self] result, error in - guard let self = self else { return } - if let result = result { - self.resultSubject.send(result) - if result.isFinal { - self.resultSubject.send(completion: .finished) + // Create and configure the speech recognition request. + recognitionRequest = SFSpeechAudioBufferRecognitionRequest() + guard let recognitionRequest = recognitionRequest else { fatalError("Unable to create a SFSpeechAudioBufferRecognitionRequest object") } + + // Use `sessionConfiguration` to configure the recognition request + recognitionRequest.shouldReportPartialResults = sessionConfiguration.shouldReportPartialResults + recognitionRequest.requiresOnDeviceRecognition = sessionConfiguration.requiresOnDeviceRecognition + recognitionRequest.taskHint = sessionConfiguration.taskHint + recognitionRequest.contextualStrings = sessionConfiguration.contextualStrings + recognitionRequest.interactionIdentifier = sessionConfiguration.interactionIdentifier + + // Create a recognition task for the speech recognition session. + // Keep a reference to the task so that it can be cancelled. + recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest) { [weak self] result, error in + guard let self = self else { return } + if let result = result { + self.resultSubject.send(result) + if result.isFinal { + self.resultSubject.send(completion: .finished) + SpeechRecognizer.remove(id: self.id) + } + } else if let error = error { + self.stopRecording() + self.resultSubject.send(completion: .failure(error)) SpeechRecognizer.remove(id: self.id) + } else { + fatalError("No result and no error") } - } else if let error = error { - self.stopRecording() - self.resultSubject.send(completion: .failure(error)) - SpeechRecognizer.remove(id: self.id) - } else { - fatalError("No result and no error") } - } - // Configure the microphone input. - let recordingFormat = inputNode.outputFormat(forBus: 0) - inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer: AVAudioPCMBuffer, when: AVAudioTime) in - self.recognitionRequest?.append(buffer) + // Configure the microphone input. + 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() + } catch { + resultSubject.send(completion: .failure(error)) + SpeechRecognizer.remove(id: self.id) } - - audioEngine.prepare() - try audioEngine.start() - } public func stopRecording() { diff --git a/Sources/SwiftSpeech/ViewModifiers.swift b/Sources/SwiftSpeech/ViewModifiers.swift index 00f694d..5553d07 100644 --- a/Sources/SwiftSpeech/ViewModifiers.swift +++ b/Sources/SwiftSpeech/ViewModifiers.swift @@ -106,8 +106,8 @@ public extension SwiftSpeech.ViewModifiers { // View update self.viewComponentState = .recording self.recordingSession = session - try! session.startRecording() delegate.onStartRecording(session: session) + session.startRecording() } fileprivate func cancelRecording() { @@ -173,8 +173,8 @@ public extension SwiftSpeech.ViewModifiers { // View update self.viewComponentState = .recording self.recordingSession = session - try! session.startRecording() delegate.onStartRecording(session: session) + session.startRecording() } fileprivate func endRecording() {