語音識別
坑一、授權的幾個枚舉一定要注意千萬別寫錯了,細心點莫有問題!!
語音合成
iOS7.0 后蘋果推出的
神坑、在設置AVAudioSession的分類是一定要注意其類型。
/* 使用這一類別的背景聲音,如雨,汽車引擎噪音等?;旌吓c其他音樂。 */
AVAudioSessionCategoryAmbient;
/* 使用這個類別作為背景聲音。其他音樂也會停止演奏。*/
AVAudioSessionCategorySoloAmbient;
/* 用這一類別來播放音樂。*/
AVAudioSessionCategoryPlayback;
/* 在錄制音頻時使用這個類別.*/
AVAudioSessionCategoryRecord;
/* 在錄制和回放音頻時使用這一類別。 */
AVAudioSessionCategoryPlayAndRecord;
/* 當使用硬件編解碼器或信號處理器時,請使用這一類別不播放或錄制音頻。 */
AVAudioSessionCategoryAudioProcessing
//設置方法,因為我們還需要識別播放,所以選擇這個AVAudioSessionCategoryPlayAndRecord類型,使用別的播放會報錯 Failure starting audio queue
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
注意:使用AVAudioSessionCategoryPlayAndRecord類型默認是聽筒播放,設置為揚聲器有兩種方法:
方法一:可以用該方法來實現(xiàn)聽筒和揚聲器切換播放
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:nil];
方法二
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:nil];
1、獲取權限,將下面的代碼放到Info.plist中
<key>NSMicrophoneUsageDescription</key> <string>App需要您的同意,才能訪問麥克風</string> <key>NSSpeechRecognitionUsageDescription</key> <string>App需要您的同意,才能使用語音識別技術</string>
2、幾個注意
//請求授權使用的枚舉
//SFSpeechRecognizerAuthorizationStatusNotDetermined : 語音識別未授權
//SFSpeechRecognizerAuthorizationStatusDenied :用戶未授權使用語音識別
//SFSpeechRecognizerAuthorizationStatusRestricted :語音識別在這臺設備上受到限制
//SFSpeechRecognizerAuthorizationStatusAuthorized :已授權
詳解AVAudioSession參考:(http://www.itdecent.cn/p/331a4455ba7f)
3、全部代碼
//
// SpeechRecognitionViewController.m
// SpeechRecognition
//
// Created by 李東 on 2017/8/22.
// Copyright ? 2017年 LD. All rights reserved.
//
#import "SpeechRecognitionViewController.h"
#import <Speech/Speech.h>
#import <AVFoundation/AVFoundation.h>
@interface SpeechRecognitionViewController () <SFSpeechRecognizerDelegate>
//開始錄音按鈕
@property (weak, nonatomic) IBOutlet UIButton *recordButton;
//展示結果
@property (weak, nonatomic) IBOutlet UILabel *showResults;
//語音識別器
@property (nonatomic, strong) SFSpeechRecognizer *speechRecognizer;
// 語音識別任務,可監(jiān)控識別進度。通過他可以取消或終止當前的語音識別任務
@property (nonatomic, strong) SFSpeechRecognitionTask *speechRecognitionTask;
// 語音引擎,負責提供錄音輸入
@property (nonatomic,strong) AVAudioEngine *audioEngine;
//從任意緩存中獲取語音的識別請求
// 發(fā)起語音識別請求,為語音識別器指定一個音頻輸入源,這里是在音頻緩沖器中提供的識別語音。
// SFSpeechAudioBufferRecognitionRequest 從任意緩存中獲取語音的識別請求
// SFSpeechRecognitionRequest 從音頻源識別語音的請求。
// SFSpeechURLRecognitionRequest 在錄制的音頻文件中識別語音的請求。
@property (nonatomic,strong) SFSpeechAudioBufferRecognitionRequest *recognitionRequest;
//語音合成器,負責文本轉語音的播放
@property (nonatomic, strong) AVSpeechSynthesizer * avSS;
@end
@implementation SpeechRecognitionViewController
- (void)viewDidLoad {
[super viewDidLoad];
_recordButton.enabled = NO;
// Do any additional setup after loading the view from its nib.
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
//請求授權,使用的枚舉
//SFSpeechRecognizerAuthorizationStatusNotDetermined : 語音識別未授權
//SFSpeechRecognizerAuthorizationStatusDenied :用戶未授權使用語音識別
//SFSpeechRecognizerAuthorizationStatusRestricted :語音識別在這臺設備上受到限制
//SFSpeechRecognizerAuthorizationStatusAuthorized :已授權
__weak typeof(self) weakSelf = self;
[SFSpeechRecognizer requestAuthorization:^(SFSpeechRecognizerAuthorizationStatus status) {
dispatch_async(dispatch_get_main_queue(), ^{
switch (status) {
case SFSpeechRecognizerAuthorizationStatusNotDetermined:
weakSelf.recordButton.enabled = NO;
[weakSelf.recordButton setTitle:@"語音識別未授權" forState: UIControlStateDisabled];
break;
case SFSpeechRecognizerAuthorizationStatusDenied:
weakSelf.recordButton.enabled = NO;
[weakSelf.recordButton setTitle:@"用戶未授權使用語音識別" forState: UIControlStateDisabled];
break;
case SFSpeechRecognizerAuthorizationStatusRestricted:
weakSelf.recordButton.enabled = NO;
[weakSelf.recordButton setTitle:@"語音識別在這臺設備上受到限制" forState: UIControlStateDisabled];
break;
case SFSpeechRecognizerAuthorizationStatusAuthorized:
weakSelf.recordButton.enabled = YES;
[weakSelf.recordButton setTitle:@"開始錄音" forState: UIControlStateNormal];
break;
default:
break;
}
});
}];
}
//錄音按鈕點擊方法
- (IBAction)recordButtonClick:(UIButton *)sender {
if (self.audioEngine.isRunning) {
// 停止錄音
[self.audioEngine stop];
if (_recognitionRequest) {
[_recognitionRequest endAudio];
}
self.recordButton.enabled = NO;
[self.recordButton setTitle:@"正在停止。。。" forState: UIControlStateDisabled];
} else {
[self startRecording];
[self.recordButton setTitle:@"停止錄音" forState:UIControlStateNormal];
}
}
//播放識別后的文本
- (IBAction)stratPlaySpeech:(id)sender {
[self readingAloudWithString:self.showResults.text language:@"zh-CN"];
}
//設置為揚聲器播放
- (IBAction)setupeRceiver:(id)sender {
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:nil];
}
//設置聽筒播放
- (IBAction)setupSpeaker:(id)sender {
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession overrideOutputAudioPort:AVAudioSessionPortOverrideNone error:nil];
}
//開始錄音方法
- (void)startRecording {
//判斷上一個任務是否還在,制空
if (_speechRecognitionTask) {
[_speechRecognitionTask cancel];
_speechRecognitionTask = nil;
}
//創(chuàng)建錄音session
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *error;
//設置會話的類別,默認是聽筒播放
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
//設置為揚聲器播放
//[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:nil];
NSParameterAssert(!error);
//設置模式
[audioSession setMode:AVAudioSessionModeMeasurement error:&error];
NSParameterAssert(!error);
[audioSession setActive:YES withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&error];
NSParameterAssert(!error);
//初始化識別請求
_recognitionRequest = [[SFSpeechAudioBufferRecognitionRequest alloc] init];
AVAudioInputNode *inputNode = self.audioEngine.inputNode;
NSAssert(inputNode, @"錄音設備沒有準備好");
NSAssert(_recognitionRequest, @"請求初始化失敗");
_recognitionRequest.shouldReportPartialResults = YES;
__weak typeof(self) weakSelf = self;
_speechRecognitionTask = [self.speechRecognizer recognitionTaskWithRequest:_recognitionRequest resultHandler:^(SFSpeechRecognitionResult * _Nullable result, NSError * _Nullable error) {
__strong typeof(weakSelf) strongSelf = weakSelf;
BOOL isFinall = NO;
if (result) {
//拿到全部的結果
// NSLog(@"%@", result);
// NSLog(@"%@", result.bestTranscription.formattedString);
//strongSelf.showResults.text = result.bestTranscription.formattedString;
//根據result.isFinal判斷是不是最后的結果
isFinall = result.isFinal;
if (result.isFinal) {
NSLog(@"我是最后的結果");
strongSelf.showResults.text = result.bestTranscription.formattedString;
}
}
if (error || isFinall) {
[strongSelf.audioEngine stop];
[inputNode removeTapOnBus:0];
strongSelf.recognitionRequest = nil;
strongSelf.speechRecognitionTask = nil;
strongSelf.recordButton.enabled = YES;
[strongSelf.recordButton setTitle:@"開始錄音" forState:UIControlStateNormal];
}
}];
AVAudioFormat *recordingFormat = [inputNode outputFormatForBus:0];
//在添加tap之前先移除上一個 不然有可能報"Terminating app due to uncaught exception 'com.apple.coreaudio.avfaudio',"之類的錯誤
[inputNode removeTapOnBus:0];
[inputNode installTapOnBus:0 bufferSize:1024 format:recordingFormat block:^(AVAudioPCMBuffer * _Nonnull buffer, AVAudioTime * _Nonnull when) {
__strong typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf.recognitionRequest) {
[strongSelf.recognitionRequest appendAudioPCMBuffer:buffer];
}
}];
[self.audioEngine prepare];
[self.audioEngine startAndReturnError:&error];
NSParameterAssert(!error);
self.showResults.text = @"正在錄音。。。";
}
//使用系統(tǒng)的語音合成
- (void)readingAloudWithString:(NSString *)str language: (NSString *)language{
//說話的內容
AVSpeechUtterance *utterance = [[AVSpeechUtterance alloc] initWithString:str];
//語調0.5到2,默認1
utterance.pitchMultiplier = 1;
//語速 0到1,默認是0.5
utterance.rate = 0.5;
//音量
utterance.volume = 1;
AVSpeechSynthesisVoice *voiceType = [AVSpeechSynthesisVoice voiceWithLanguage:language];
utterance.voice = voiceType;
[self.avSS speakUtterance:utterance];
}
#pragma mark - 懶加載
//語音合成器
- (AVSpeechSynthesizer *)avSS {
if (!_avSS) {
_avSS = [[AVSpeechSynthesizer alloc] init];
_avSS.delegate = self;
}
return _avSS;
}
//語音引擎,負責提供錄音輸入
- (AVAudioEngine *)audioEngine {
if (!_audioEngine) {
_audioEngine = [[AVAudioEngine alloc] init];
}
return _audioEngine;
}
//懶加載語音識別器
- (SFSpeechRecognizer *)speechRecognizer {
if (!_speechRecognizer) {
//為識別語音設置語言這里暫時設置為中文
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"zh-CN"];
_speechRecognizer = [[SFSpeechRecognizer alloc] initWithLocale:locale];
_speechRecognizer.delegate = self;
}
return _speechRecognizer;
}
#pragma mark - SFSpeechRecognizerDelegate
- (void)speechRecognizer:(SFSpeechRecognizer *)speechRecognizer availabilityDidChange:(BOOL)available {
if (available) {
self.recordButton.enabled = YES;
[self.recordButton setTitle:@"開始錄音" forState: UIControlStateNormal];
} else {
self.recordButton.enabled = NO;
[self.recordButton setTitle:@"語音識別不可用" forState:UIControlStateDisabled];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end