AutoCat/AutoCat/Utils/Recorder.swift
Selim Mustafaev f513222d72 Voice recording fixes.
Adding experimental siri support
2020-06-28 20:30:28 +03:00

123 lines
4.9 KiB
Swift

import Foundation
import Speech
import AVFoundation
import AudioToolbox
class Recorder {
let session = AVAudioSession.sharedInstance()
let engine = AVAudioEngine()
var fileRef: ExtAudioFileRef? = nil
let recognizer = SFSpeechRecognizer(locale: Locale(identifier: "ru_RU"))
let request = SFSpeechAudioBufferRecognitionRequest()
var recognitionTask: SFSpeechRecognitionTask?
var endRecognitionTimer: Timer?
var result: String = ""
let recordingSettings: [String:Any] = [
AVFormatIDKey:kAudioFormatMPEG4AAC_HE,
AVSampleRateKey:44100.0,
AVNumberOfChannelsKey:2,
//AVEncoderBitRateKey:320*1024,
//AVLinearPCMBitDepthKey:16,
AVEncoderAudioQualityKey:AVAudioQuality.max.rawValue
]
init() throws {
try self.session.setCategory(.record, mode: .spokenAudio, options: .mixWithOthers)
}
func requestPermissions(completion: @escaping (NSError?) -> Void) {
self.session.requestRecordPermission { allowed in
if allowed {
SFSpeechRecognizer.requestAuthorization { status in
switch status {
case .authorized:
completion(nil)
break
case .denied:
let error = CocoaError.error("Access denied", suggestion: "Please give permission to use speech recognition in system settings")
completion(error)
break
case .restricted:
let error = CocoaError.error("Access restricted", suggestion: "Speech recognition is restricted on this device")
completion(error)
break
case .notDetermined:
let error = CocoaError.error("Access error", suggestion: "Speech recognition status is not yet determined")
completion(error)
break
@unknown default:
let error = CocoaError.error("Access error", suggestion: "Unknown error accessing speech recognizer")
completion(error)
break
}
}
} else {
let error = CocoaError.error("Access denied", suggestion: "Please give permission to use microphone in system settings")
completion(error)
}
}
}
func startRecording(to file: URL, completion: @escaping (String) -> Void) throws {
let inFormat = self.engine.inputNode.outputFormat(forBus: 0)
guard let aac = AVAudioFormat(settings: self.recordingSettings) else {
throw CocoaError.error("Recording error", suggestion: "Format not supported")
}
ExtAudioFileCreateWithURL(file as CFURL, kAudioFileM4AType, aac.streamDescription, nil, AudioFileFlags.eraseFile.rawValue, &fileRef)
guard let fileRef = self.fileRef else {
throw CocoaError.error(CocoaError.Code.fileWriteUnknown)
}
// Play sound (sms_alert_note.caf) to indicate start of recording
AudioServicesPlayAlertSound(SystemSoundID(4097))
ExtAudioFileSetProperty(fileRef, kExtAudioFileProperty_ClientDataFormat, UInt32(MemoryLayout<AudioStreamBasicDescription>.size), inFormat.streamDescription)
self.engine.inputNode.installTap(onBus: 0, bufferSize: 1024, format: inFormat) { buffer, time in
self.request.append(buffer)
//print(self.recognitionTask?.state.rawValue)
ExtAudioFileWrite(fileRef, buffer.frameLength, buffer.audioBufferList)
}
self.recognitionTask = self.recognizer!.recognitionTask(with: self.request) { result, error in
if let transcription = result?.bestTranscription {
self.result = transcription.formattedString
self.endRecognitionTimer?.invalidate()
self.endRecognitionTimer = Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { timer in
self.stopRecording()
completion(self.result)
}
}
}
self.endRecognitionTimer = Timer.scheduledTimer(withTimeInterval: 5, repeats: false) { timer in
self.stopRecording()
completion(self.result)
}
self.engine.prepare()
try self.engine.start()
}
func cancelRecording() {
self.stopRecording()
self.endRecognitionTimer?.invalidate()
self.endRecognitionTimer = nil
}
func stopRecording() {
self.engine.stop()
self.engine.inputNode.removeTap(onBus: 0)
self.request.endAudio()
self.recognitionTask?.cancel()
if let fileRef = self.fileRef {
ExtAudioFileDispose(fileRef)
}
}
}