闲来无聊翻看苹果官方文档的时候看到一个 Speech 框架 ,之前并没有接触过这个框架,看了一下这个框架其实不大,于是决定研究一下。 Speech API可让我们扩展和增强应用程序中的语音识别体验,而无需键盘,就可以实现语音输入这样的功能。既然涉及到语音这些敏感的数据,必然是要通过用户授权的,也就是说 info.plist 文件中需要添加响应的键值来获取用户权限。因为语音识别并不是在iOS 设备本地进行识别,而是在苹果的伺服器上进行识别的。所有的语音数据都需要传给苹果的后台服务器进行处理。因此必须得到用户的授权。

<key>NSMicrophoneUsageDescription</key>
	<string>当你按下 开始 按钮,我们将使用麦克风进行录音</string>
    <key>NSSpeechRecognitionUsageDescription</key>
    <string>当你对着麦克风说话时,我们将开始语音识别</string>
复制代码

语音识别是基于网络的服务,因此强制执行限制,以便所有应用程序可以免费使用该服务。每个设备可能会限制每天可以执行的识别次数,并且每个应用程序可以根据每天的请求数量在全球范围内进行限制。例如,如果识别请求快速失败(在启动的一秒或两秒内),则识别服务可能暂时无法应用到您的应用程序中,您可能希望稍后再次请求用户。 此外语音识别可以为电池寿命和网络使用造成相对较高的负担。在iOS 10中,话音音频持续时间限制在约1分钟,这与键盘相关听写的限制相似。 对于Speech 的使用姿势官方是给出了案例的,可以来 这里看 。当然你也可以看我的 小 Demo ,欢迎 Star鼓励。

下面给出我的代码和一些代码注释:

//
//  ViewController.swift
//  YaHoYiSpeech
//
//  Created by 太阳在线YHY on 2017/6/10.
//  Copyright © 2017年 太阳在线. All rights reserved.
//

import UIKit
import Speech

class ViewController: UIViewController {

	@IBOutlet weak var textView: UITextView!
	
	@IBOutlet weak var startBtn: UIButton!
	
	
	//  在进行语音识别之前,你必须获得用户的相应授权,因为语音识别并不是在iOS 设备本地进行识别,而是在苹果的伺服器上进行识别的。所有的语音数据都需要传给苹果的后台服务器进行处理。因此必须得到用户的授权。

	// 创建语音识别器,指定语音识别的语言环境 locale ,将来会转化为什么语言,这里是使用的当前区域,那肯定就是简体汉语啦
	private let speechRecognizer = SFSpeechRecognizer(locale: Locale.autoupdatingCurrent)
	// 使用 identifier 这里设置的区域是台湾,将来会转化为繁体汉语
	//	private let speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "zh-TW"))
	
	
	// 发起语音识别请求,为语音识别器指定一个音频输入源,这里是在音频缓冲器中提供的识别语音。
	// 除 SFSpeechAudioBufferRecognitionRequest 之外还包括:
	// SFSpeechRecognitionRequest  从音频源识别语音的请求。
	// SFSpeechURLRecognitionRequest 在录制的音频文件中识别语音的请求。
	private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?
	// 语音识别任务,可监控识别进度。通过他可以取消或终止当前的语音识别任务
	private var recognitionTask: SFSpeechRecognitionTask?
	// 语音引擎,负责提供录音输入
	private let audioEngine = AVAudioEngine()
	
	override func viewDidLoad() {
		super.viewDidLoad()
		
		// 输出一下语音识别器支持的区域,就是上边初始化SFSpeechRecognizer 时 locale 所需要的 identifier
	    print(SFSpeechRecognizer.supportedLocales())
		
		startBtn.isEnabled = false
		// 设置语音识别器代理
		speechRecognizer?.delegate = self
		
		// 要求用户授予您的应用许可来执行语音识别。
		SFSpeechRecognizer.requestAuthorization { (status) in
			var isButtonenable = false
			// 识别器的授权状态
			switch status {
			    // 经过授权
				case .authorized:
					// 经过授权之后就允许录音按钮点击
					isButtonenable = true
			    // 拒绝授权
				case .denied:
					isButtonenable = false
					print("User denied access to speech recognition")
			    // 保密,也就是不授权
				case .restricted:
					isButtonenable = false
					print("Speech recognition restricted on this device")
			    // 未决定
				case .notDetermined:
					isButtonenable = false
					print("Speech recognition not yet authorized")
				}
				
				OperationQueue.main.addOperation() {
					self.startBtn.isEnabled = isButtonenable
				}
		}
	}

	
	func startRecordingPersonSpeech() {
		// 检查 recognitionTask 任务是否处于运行状态。如果是,取消任务开始新的任务
		if recognitionTask != nil {
			// 取消当前语音识别任务。
			recognitionTask?.cancel()
			// 语音识别任务的当前状态 是一个枚举值
			print(recognitionTask!.state)
			
			recognitionTask = nil
			
		}
		
		// 建立一个AVAudioSession 用于录音
		let audioSession = AVAudioSession.sharedInstance()
		do {
			// category 设置为 record,录音
			try audioSession.setCategory(AVAudioSessionCategoryRecord)
			// mode 设置为 measurement
			try audioSession.setMode(AVAudioSessionModeMeasurement)
			// 开启 audioSession
			try audioSession.setActive(true, with: .notifyOthersOnDeactivation)
		} catch {
			print("audioSession properties weren't set because of an error.")
		}
		
		// 初始化RecognitionRequest,在后边我们会用它将录音数据转发给苹果服务器
		recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
		
		// 检查 iPhone 是否有有效的录音设备
		guard let inputNode = audioEngine.inputNode else {
			fatalError("Audio engine has no input node")
		}
		
		//
		guard let recognitionRequest = recognitionRequest else {
			fatalError("Unable to create an SFSpeechAudioBufferRecognitionRequest object")
		}
		// 在用户说话的同时,将识别结果分批次返回
		recognitionRequest.shouldReportPartialResults = true
		
		// 使用recognitionTask方法开始识别。
		recognitionTask = speechRecognizer?.recognitionTask(with: recognitionRequest, resultHandler: { (result, error) in
			// 用于检查识别是否结束
			var isFinal = false
			// 如果 result 不是 nil,
			if result != nil {
				// 将 textView.text 设置为 result 的最佳音译
				self.textView.text = result?.bestTranscription.formattedString
				// 如果 result 是最终,将 isFinal 设置为 true
				isFinal = (result?.isFinal)!
			}
			
			// 如果没有错误发生,或者 result 已经结束,停止audioEngine 录音,终止 recognitionRequest 和 recognitionTask
			if error != nil || isFinal {
				self.audioEngine.stop()
				inputNode.removeTap(onBus: 0)
				
				self.recognitionRequest = nil
				self.recognitionTask = nil
				// 开始录音按钮可用
				self.startBtn.isEnabled = true
			}
		})
		
		// 向recognitionRequest加入一个音频输入
		let recordingFormat = inputNode.outputFormat(forBus: 0)
		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.")
		}
		
		textView.text = "请讲话!"
		
	}
	
	
	@IBAction func startRecording(_ sender: UIButton) {
		if audioEngine.isRunning {
			// 停止录音
			audioEngine.stop()
			// 表示音频源已完成,并且不会再将音频附加到识别请求。
			recognitionRequest?.endAudio()
			startBtn.isEnabled = false
			startBtn.setTitle("开始", for: .normal)
		} else {
			startRecordingPersonSpeech()
			startBtn.setTitle("结束", for: .normal)
		}
	}
}

extension ViewController: SFSpeechRecognizerDelegate {
	func speechRecognizer(_ speechRecognizer: SFSpeechRecognizer, availabilityDidChange available: Bool) {
		if available {
			startBtn.isEnabled = true
		}else {
			startBtn.isEnabled = false
		}
	}
}

复制代码