123 lines
4.9 KiB
Swift
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)
|
|
}
|
|
}
|
|
}
|