1. 程式人生 > >iOS10語音識別框架SpeechFramework應用

iOS10語音識別框架SpeechFramework應用

一、引言

iOS10系統是一個較有突破性的系統,其在Message,Notification等方面都開放了很多實用性的開發介面。本篇部落格將主要探討iOS10中新引入的SpeechFramework框架。有個這個框架,開發者可以十分容易的為自己的App新增語音識別功能,不需要再依賴於其他第三方的語音識別服務,並且,Apple的Siri應用的強大也證明了Apple的語音服務是足夠強大的,不通過第三方,也大大增強了使用者的安全性。

二、SpeechFramework框架中的重要類

SpeechFramework框架比較輕量級,其中的類並不十分冗雜,在學習SpeechFramework框架前,我們需要對其中類與類與類之間的關係有個大致的熟悉瞭解。

SFSpeechRecognizer:這個類是語音識別的操作類,用於語音識別使用者許可權的申請,語言環境的設定,語音模式的設定以及向Apple服務傳送語音識別的請求。

SFSpeechRecognitionTask:這個類是語音識別服務請求任務類,每一個語音識別請求都可以抽象為一個SFSpeechRecognitionTask例項,其中SFSpeechRecognitionTaskDelegate協議中約定了許多請求任務過程中的監聽方法。

SFSpeechRecognitionRequest:語音識別請求類,需要通過其子類來進行例項化。

SFSpeechURLRecognitionRequest:通過音訊URL來建立語音識別請求。

SFSpeechAudioBufferRecognitionRequest:通過音訊流來建立語音識別請求。

SFSpeechRecognitionResult:語音識別請求結果類。

SFTranscription:語音轉換後的資訊類。

SFTranscriptionSegment:語音轉換中的音訊節點類。

瞭解了上述類的作用於其之間的聯絡,使用SpeechFramework框架將十分容易。

三、申請使用者語音識別許可權與進行語音識別請求

開發者若要在自己的App中使用語音識別功能,需要獲取使用者的同意。首先需要在工程的Info.plist檔案中新增一個Privacy-Speech Recognition Usage Description鍵,其實需要對應一個String型別的值,這個值將會在系統獲取許可權的警告框中顯示,Info.plist檔案如下圖所示: 
這裡寫圖片描述

 
使用SFSpeechRecognize類的requestAuthorization方法來進行使用者許可權的申請,使用者的反饋結果會在這個方法的回撥block中傳入,如下:

  //申請使用者語音識別許可權
  [SFSpeechRecognizer requestAuthorization:^(SFSpeechRecognizerAuthorizationStatus status) {     
  }];
  • 1
  • 2
  • 3

SFSpeechRecognizerAuthorzationStatus列舉中定義了使用者的反饋結果,如下:

typedef NS_ENUM(NSInteger, SFSpeechRecognizerAuthorizationStatus) {
    //結果未知 使用者尚未進行選擇
    SFSpeechRecognizerAuthorizationStatusNotDetermined,
    //使用者拒絕授權語音識別
    SFSpeechRecognizerAuthorizationStatusDenied,
    //裝置不支援語音識別功能
    SFSpeechRecognizerAuthorizationStatusRestricted,
    //使用者授權語音識別
    SFSpeechRecognizerAuthorizationStatusAuthorized,
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

如果申請使用者語音識別許可權成功,開發者可以通過SFSpeechRecognizer操作類來進行語音識別請求,示例如下:

    //建立語音識別操作類物件
    SFSpeechRecognizer * rec = [[SFSpeechRecognizer alloc]init];
    //通過一個音訊路徑建立音訊識別請求
    SFSpeechRecognitionRequest * request = [[SFSpeechURLRecognitionRequest alloc]initWithURL:[[NSBundle mainBundle] URLForResource:@"7011" withExtension:@"m4a"]];
    //進行請求
    [rec recognitionTaskWithRequest:request resultHandler:^(SFSpeechRecognitionResult * _Nullable result, NSError * _Nullable error) {
        //列印語音識別的結果字串
        NSLog(@"%@",result.bestTranscription.formattedString);
    }];
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

四、深入SFSpeechRecognizer類

SFSpeechRecognizer類的主要作用是申請許可權,配置引數與進行語音識別請求。其中比較重要的屬性與方法如下:

//獲取當前使用者許可權狀態
+ (SFSpeechRecognizerAuthorizationStatus)authorizationStatus;
//申請語音識別使用者許可權
+ (void)requestAuthorization:(void(^)(SFSpeechRecognizerAuthorizationStatus status))handler;
//獲取所支援的所有語言環境
+ (NSSet<NSLocale *> *)supportedLocales;
//初始化方法 需要注意 這個初始化方法將預設以裝置當前的語言環境作為語音識別的語言環境
- (nullable instancetype)init;
//初始化方法 設定一個特定的語言環境
- (nullable instancetype)initWithLocale:(NSLocale *)locale NS_DESIGNATED_INITIALIZER;
//語音識別是否可用
@property (nonatomic, readonly, getter=isAvailable) BOOL available;
//語音識別操作類協議代理
@property (nonatomic, weak) id<SFSpeechRecognizerDelegate> delegate;
//設定語音識別的配置引數 需要注意 在每個語音識別請求中也有這樣一個屬性 這裡設定將作為預設值
//如果SFSpeechRecognitionRequest物件中也進行了設定 則會覆蓋這裡的值
/*
typedef NS_ENUM(NSInteger, SFSpeechRecognitionTaskHint) {
    SFSpeechRecognitionTaskHintUnspecified = 0,     // 無定義
    SFSpeechRecognitionTaskHintDictation = 1,       // 正常的聽寫風格
    SFSpeechRecognitionTaskHintSearch = 2,          // 搜尋風格
    SFSpeechRecognitionTaskHintConfirmation = 3,    // 短語風格
};
*/
@property (nonatomic) SFSpeechRecognitionTaskHint defaultTaskHint;
//使用回撥Block的方式進行語音識別請求 請求結果會在Block中傳入
- (SFSpeechRecognitionTask *)recognitionTaskWithRequest:(SFSpeechRecognitionRequest *)request
                                          resultHandler:(void (^)(SFSpeechRecognitionResult * __nullable result, NSError * __nullable error))resultHandler;
//使用代理回撥的方式進行語音識別請求
- (SFSpeechRecognitionTask *)recognitionTaskWithRequest:(SFSpeechRecognitionRequest *)request
                                               delegate:(id <SFSpeechRecognitionTaskDelegate>)delegate;
//設定請求所佔用的任務佇列
@property (nonatomic, strong) NSOperationQueue *queue;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

SFSpeechRecognizerDelegate協議中只約定了一個方法,如下:

//當語音識別操作可用性發生改變時會被呼叫
- (void)speechRecognizer:(SFSpeechRecognizer *)speechRecognizer availabilityDidChange:(BOOL)available;
  • 1
  • 2

通過Block回撥的方式進行語音識別請求十分簡單,如果使用代理回撥的方式,開發者需要實現SFSpeechRecognitionTaskDelegate協議中的相關方法,如下:

//當開始檢測音訊源中的語音時首先呼叫此方法
- (void)speechRecognitionDidDetectSpeech:(SFSpeechRecognitionTask *)task;
//當識別出一條可用的資訊後 會呼叫
/*
需要注意,apple的語音識別服務會根據提供的音訊源識別出多個可能的結果 每有一條結果可用 都會呼叫此方法
*/
- (void)speechRecognitionTask:(SFSpeechRecognitionTask *)task didHypothesizeTranscription:(SFTranscription *)transcription;
//當識別完成所有可用的結果後呼叫
- (void)speechRecognitionTask:(SFSpeechRecognitionTask *)task didFinishRecognition:(SFSpeechRecognitionResult *)recognitionResult;
//當不再接受音訊輸入時呼叫 即開始處理語音識別任務時呼叫
- (void)speechRecognitionTaskFinishedReadingAudio:(SFSpeechRecognitionTask *)task;
//當語音識別任務被取消時呼叫
- (void)speechRecognitionTaskWasCancelled:(SFSpeechRecognitionTask *)task;
//語音識別任務完成時被呼叫
- (void)speechRecognitionTask:(SFSpeechRecognitionTask *)task didFinishSuccessfully:(BOOL)successfully;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

SFSpeechRecognitionTask類中封裝了屬性和方法如下:

//此任務的當前狀態
/*
typedef NS_ENUM(NSInteger, SFSpeechRecognitionTaskState) {
    SFSpeechRecognitionTaskStateStarting = 0,       // 任務開始
    SFSpeechRecognitionTaskStateRunning = 1,        // 任務正在執行
    SFSpeechRecognitionTaskStateFinishing = 2,      // 不在進行音訊讀入 即將返回識別結果
    SFSpeechRecognitionTaskStateCanceling = 3,      // 任務取消
    SFSpeechRecognitionTaskStateCompleted = 4,      // 所有結果返回完成
};
*/
@property (nonatomic, readonly) SFSpeechRecognitionTaskState state;
//音訊輸入是否完成
@property (nonatomic, readonly, getter=isFinishing) BOOL finishing;
//手動完成音訊輸入 不再接收音訊
- (void)finish;
//任務是否被取消
@property (nonatomic, readonly, getter=isCancelled) BOOL cancelled;
//手動取消任務
- (void)cancel;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

關於音訊識別請求類,除了可以使用SFSpeechURLRecognitionRequest類來進行建立外,還可以使用SFSpeechAudioBufferRecognitionRequest類來進行建立:

@interface SFSpeechAudioBufferRecognitionRequest : SFSpeechRecognitionRequest

@property (nonatomic, readonly) AVAudioFormat *nativeAudioFormat;
//拼接音訊流
- (void)appendAudioPCMBuffer:(AVAudioPCMBuffer *)audioPCMBuffer;
- (void)appendAudioSampleBuffer:(CMSampleBufferRef)sampleBuffer;
//完成輸入
- (void)endAudio;

@end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

五、語音識別結果類SFSpeechRecognitionResult

SFSpeechRecognitionResult類是語音識別結果的封裝,其中包含了許多套平行的識別資訊,其每一份識別資訊都有可信度屬性來描述其準確程度。SFSpeechRecognitionResult類中屬性如下:

//識別到的多套語音轉換資訊陣列 其會按照準確度進行排序
@property (nonatomic, readonly, copy) NSArray<SFTranscription *> *transcriptions;
//準確性最高的識別例項
@property (nonatomic, readonly, copy) SFTranscription *bestTranscription;
//是否已經完成 如果YES 則所有所有識別資訊都已經獲取完成
@property (nonatomic, readonly, getter=isFinal) BOOL final;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

SFSpeechRecognitionResult類只是語音識別結果的一個封裝,真正的識別資訊定義在SFTranscription類中,SFTranscription類中屬性如下:

//完整的語音識別準換後的文字資訊字串
@property (nonatomic, readonly, copy) NSString *formattedString;
//語音識別節點陣列
@property (nonatomic, readonly, copy) NSArray<SFTranscriptionSegment *> *segments;
  • 1
  • 2
  • 3
  • 4

當對一句完整的話進行識別時,Apple的語音識別服務實際上會把這句語音拆分成若干個音訊節點,每個節點可能為一個單詞,SFTranscription類中的segments屬性就存放這些節點。SFTranscriptionSegment類中定義的屬性如下:

//當前節點識別後的文字資訊
@property (nonatomic, readonly, copy) NSString *substring;
//當前節點識別後的文字資訊在整體識別語句中的位置
@property (nonatomic, readonly) NSRange substringRange;
//當前節點的音訊時間戳
@property (nonatomic, readonly) NSTimeInterval timestamp;
//當前節點音訊的持續時間
@property (nonatomic, readonly) NSTimeInterval duration;
//可信度/準確度 0-1之間
@property (nonatomic, readonly) float confidence;
//關於此節點的其他可能的識別結果 
@property (nonatomic, readonly) NSArray<NSString *> *alternativeSubstrings;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

溫馨提示:SpeechFramework框架在模擬器上執行會出現異常情況,無法進行語音識別請求。會報出kAFAssistantErrorDomain的錯誤,還望有知道解決方案的朋友,給些建議,Thanks。