IOS音視頻(四十六)Asr siri 離線在線語音識別方案

IOS音視頻(一)AVFoundation核心類html

IOS音視頻(二)AVFoundation視頻捕捉python

IOS音視頻(三)AVFoundation 播放和錄音linux

IOS音視頻(四十三)AVFoundation 之 Audio Sessionandroid

IOS音視頻(四十四)AVFoundation 之 Audio Queue Servicesios

IOS音視頻(四十五)HTTPS 自簽名證書 實現邊下邊播c++

IOS音視頻(四十六)離線在線語音識別方案git

IOS音視頻(四十六)離線在線語音識別方案

最近作了一個語音識別相關的研究,由於公司須要使用離線語音識別功能,爲了兼顧性能和價格方面的問題,最終選擇的方案是,在線時使用siri,離線使用百度語音識別方案。github

封裝了一個離線在線合成的SDK:語音識別SDK 這個Demo裏面沒有上傳百度libBaiduSpeechSDK.a 文件,由於這個文件太大了超過了100M,沒法上傳到Git,須要本身從官方SDK下載替換到Demo中。算法

這裏總結一下現有的幾種離線語音識別方案shell

  • 簡易使用第三方SDK方案
方案 優勢 缺點 價格 成功率
科大訊飛 成功率高95% 價格貴,增長ipa包大小 可用買斷或按流量,一臺設備成本4元左右 成功率95%左右
百度AI 成功率比較高90% ,價格便宜,離線識別免費 ,提供了自定義模型訓練,訓練後識別率提升較多 增長ipa包大小, 識別率不高,離線只支持命令詞方式,支持的語音只有中文和英文,在線時會強制使用在線識別方式,超過免費流量後就要收費,若是欠費,什麼都用不了;離線引擎使用至少須要連一次外網 離線命令詞免費,在線識別按流量次數計算,1600元套餐包(200萬次中文識別,5萬次英文識別) 在線識別成功率95%左右,離線識別基本上達不到90%
siri 成功高,免費,原始自帶,蘋果系統自帶,不會增長包大小 有侷限性,要求IOS10以上系統才能使用siri api, IOS 系統13以上支持離線語音識別,但離線識別不支持中文識別,英文離線識別比百度的準確率高 徹底免費 在線識別率跟科大訊飛差很少95%以上,離線英文識別也有90%左右
  • 開源代碼方案
方案 優勢 缺點 說明 成功率
KALDI開源框架 KALDI是著名的開源自動語音識別(ASR)工具,這套工具提供了搭建目前工業界最經常使用的ASR模型的訓練工具,同時也提供了其餘一些子任務例如說話人驗證(speaker verification)和語種識別(language recognition)的pipeline。KALDI目前由Daniel Povey維護,他以前在劍橋作ASR相關的研究,後來去了JHU開發KALDI,目前在北京小米總部做爲語音的負責人。同時他也是另外一個著名的ASR工具HTK的主要做者之一。
CMU-Sphinx開源框架 功能包括按特定語法進行識別、喚醒詞識別、n-gram識別等等,這款語音識別開源框架相比於Kaldi比較適合作開發,各類函數上的封裝淺顯易懂,解碼部分的代碼很是容易看懂,且除開PC平臺,做者也考慮到了嵌入式平臺,Android開發也很方便,已有對應的Demo,Wiki上有基於PocketSphinx的語音評測的例子,且實時性相比Kaldi好了不少。 相比於Kaldi,使用的是GMM-HMM框架,準確率上可能會差一些;其餘雜項處理程序(如pitch提取等等)沒有Kaldi多。
HTK-Cambridage 是C語音編寫,支持win,linux,ios

方案一:Siri語音識別

Siri語音識別簡介

Siri語音識別用到的Api主要是SFSpeechRecognizer聲音處理器,是IOS 10 才提供的api,因此只有IOS 10以上才能使用,從IOS10 到 IOS13 直接蘋果只提供了在線識別方式,IOS13以後提供了離線識別方式。不過離線識別方式不支持中文模式,官方雖說支持中文,可是實際測試發現中文離線識別根本沒法識別。

Siri語音識別功能類介紹

  • 引入系統庫Speech
  • SFSpeechRecognizer聲音處理器,這個類是語音識別的操做類,用於語音識別用戶權限的申請,語言環境的設置,語音模式的設置以及向Apple服務發送語音識別的請求。 例以下面代碼會根據傳入的語言簡稱來返回一個聲音處理器,若是不支持,怎會返回nil。更多細節能夠查看官方文檔。
SFSpeechRecognizer(locale: Locale(identifier: langugeSimple))
複製代碼

經過下面的方法來獲得語音識別的結果:

open func recognitionTask(with request: SFSpeechRecognitionRequest, resultHandler: @escaping (SFSpeechRecognitionResult?, Error?) -> Void) -> SFSpeechRecognitionTask
複製代碼
  • AVAudioEngine專門用來處理聲音的數據
lazy var audioEngine: AVAudioEngine = {
        let audioEngine = AVAudioEngine()
        audioEngine.inputNode.installTap(onBus: 0, bufferSize: 1024, format: audioEngine.inputNode.outputFormat(forBus: 0)) { (buffer, audioTime) in
            // 爲語音識別請求對象添加一個AudioPCMBuffer,來獲取聲音數據
            self.recognitionRequest.append(buffer)
        }
        return audioEngine
    }()
複製代碼
  • SFSpeechAudioBufferRecognitionRequest語音識別器,經過音頻流來建立語音識別請求。:
// 語音識別器
    lazy var recognitionRequest: SFSpeechAudioBufferRecognitionRequest = {
        let recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
        return recognitionRequest
    }()
複製代碼
  • SFSpeechRecognitionTask語言識別任務管理器,啓用和關閉都要使用這個管理進行。這個類是語音識別服務請求任務類,每個語音識別請求均可以抽象爲一個SFSpeechRecognitionTask實例,其中SFSpeechRecognitionTaskDelegate協議中約定了許多請求任務過程當中的監聽方法。
public enum SFSpeechRecognitionTaskState : Int {

    case starting // Speech processing (potentially including recording) has not yet begun

    case running // Speech processing (potentially including recording) is running

    case finishing // No more audio is being recorded, but more recognition results may arrive

    case canceling // No more recognition reuslts will arrive, but recording may not have stopped yet

    case completed // No more results will arrive, and recording is stopped.
}
複製代碼

此外還有一些重要的類:

SFSpeechRecognitionRequest:語音識別請求類,須要經過其子類來進行實例化。 SFSpeechURLRecognitionRequest:經過音頻URL來建立語音識別請求。 SFSpeechRecognitionResult:語音識別請求結果類。 SFTranscription:語音轉換後的信息類。

具體詳情能夠參考蘋果官方文檔,蘋果提供了一個Swift版本的Demo:點擊這裏下載蘋果官方demo

Siri語音識別功能集成

  • OC 代碼集成:
//
// KSiriRecognizer.m
// KSpeechRecognition
//
// Created by yulu kong on 2020/4/3.
// Copyright © 2020 yulu kong. All rights reserved.
//

#import "KSiriRecognizer.h"
#import <Speech/Speech.h>
#import "KHelper.h"
#import "KError.h"

@interface KSiriRecognizer () <SFSpeechRecognizerDelegate>

@property (nonatomic, strong) AVAudioEngine *audioEngine;
@property (nonatomic, strong) SFSpeechRecognizer *recognizer;

@property (nonatomic, assign) BOOL isAvaliable;

@property (nonatomic, strong, nullable) SFSpeechRecognitionTask *currentTask;
@property (nonatomic, strong, nullable) SFSpeechAudioBufferRecognitionRequest *request;

@end

@implementation KSiriRecognizer

+ (void)requestAuthorizationWithResultHandler:(KSiriAuthorizationResultHandler)resultHandler
{
    [SFSpeechRecognizer requestAuthorization:^(SFSpeechRecognizerAuthorizationStatus status) {
        resultHandler([KHelper convertSiriAuthorizationStatus:status]);
    }];
}

- (instancetype)initWithLanguage:(KLanguage)language
{
    if (self = [super initWithLanguage:language]) {
        NSLocale *local = [KHelper localForLanguage:language];
        _recognizer = [[SFSpeechRecognizer alloc] initWithLocale:local];
        _recognizer.delegate = self;
    }
    return self;
}

- (KAuthorizationStatus)authorizationStatus
{
    return [KHelper convertSiriAuthorizationStatus:[SFSpeechRecognizer authorizationStatus]];
}

- (void)startWithResultHandler:(KRecognitionResultHandler)resultHandler errorHandler: (KErrorHandler _Nullable)errorHandler
{
    if (_currentTask != nil) {
        NSLog(@"正在識別中,請稍候。");
        return;
    }
    
    if (self.authorizationStatus != KAuthorizationStatusAuthorized) {
        errorHandler([KError notAuthorizationError]);
        return;
    }
    
    if (!_isAvaliable) {
        NSString *message = [NSString stringWithFormat:@"%@語音識別器不可用", [KHelper nameForLanguage:self.language]];
        errorHandler([KError errorWithCode:-1 message:message]);
        return;
    }
    
    AVAudioSession *audioSession = AVAudioSession.sharedInstance;
    NSError *error = nil;
    [audioSession setCategory:AVAudioSessionCategoryRecord mode:AVAudioSessionModeMeasurement options: AVAudioSessionCategoryOptionDuckOthers error:&error];
    if (error != nil) {
        errorHandler(error);
        return;
    }
    
    __block typeof(self) weakSelf = self;
    _request = [[SFSpeechAudioBufferRecognitionRequest alloc] init];
    _request.shouldReportPartialResults = YES;
    
//啓用離線識別的開關,這個屬性只有IOS13以上才支持
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
     _request.requiresOnDeviceRecognition = self.forceOffline;
#else

#endif
    
    _currentTask = [self.recognizer recognitionTaskWithRequest:_request resultHandler:^(SFSpeechRecognitionResult *result, NSError *error) {
       
        if (error == nil) {
            [weakSelf stop];
            errorHandler(error);
            return;
        }
        
        if (result != nil && !result.isFinal) {
            resultHandler([[KRecognitionResult alloc] initWithText:result.bestTranscription.formattedString isFinal:NO]);
            return;
        }
        
        if (result.isFinal) {
            [weakSelf stop];
            resultHandler([[KRecognitionResult alloc] initWithText:result.bestTranscription.formattedString isFinal:YES]);
        }
    }];
    
    // Configure the microphone input.
    AVAudioFormat *recordingFormat = [_audioEngine.inputNode outputFormatForBus:0];
    [_audioEngine.inputNode installTapOnBus:0 bufferSize:1024 format:recordingFormat block:^(AVAudioPCMBuffer * buffer, AVAudioTime *when) {
        [weakSelf.request appendAudioPCMBuffer:buffer];
    }];
    
    [_audioEngine prepare];
    
    if (![_audioEngine startAndReturnError:&error]) {
        _currentTask = nil;
        errorHandler(error);
    }
}

- (void)stop {
    
    if (_currentTask == nil || !_isAvaliable) {
        return;
    }
    
    [_currentTask cancel];
    [_audioEngine stop];
    [_request endAudio];
    _currentTask = nil;
}



- (void)speechRecognizer:(SFSpeechRecognizer *)speechRecognizer availabilityDidChange:(BOOL)available
{
    _isAvaliable = available;
}

@end

複製代碼
  • Swift5 代碼集成:
//
// JPSpeechRecognition.swift
// JimuPro
//
// Created by 孔雨露 on 2020/3/7.
// Copyright © 2020 UBTech. All rights reserved.
//

import Foundation
import UIKit
import Speech

enum JPSpeechType: Int {
    case start
    case stop
    case finished
    case authDenied
}

typealias JPSpeechBlock = (_ speechType: JPSpeechType, _ finalText: String?) -> Void

@available(iOS 10.0, *)

class JPSpeechRecognition: NSObject {

    //private var parentVc: UIViewController!
    private var speechTask: SFSpeechRecognitionTask?
    // 聲音處理器
    private var speechRecognizer: SFSpeechRecognizer?
    
    private var block: JPSpeechBlock?
    
    // 語音識別器
    var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?
    
 
    
    lazy var audioEngine: AVAudioEngine = {
        let audioEngine = AVAudioEngine()
        audioEngine.inputNode.installTap(onBus: 0, bufferSize: 1024, format: audioEngine.inputNode.outputFormat(forBus: 0)) { (buffer, audioTime) in
            // 爲語音識別請求對象添加一個AudioPCMBuffer,來獲取聲音數據
            if let recognitionRequest = self.recognitionRequest {
                recognitionRequest.append(buffer)
            }
        }
        return audioEngine
    }()
    
    
    func startSpeech(languge: String, speechBlock: @escaping JPSpeechBlock) {
        //parentVc = speechVc
        block = speechBlock
        setAudioActive()
        checkmicroPhoneAuthorization { (microStatus) in
            if microStatus {
                self.checkRecognizerAuthorization(recongStatus: { (recStatus) in
                    if recStatus {
                        // 初始化語音處理器的輸入模式 語音處理器準備就緒(會爲一些audioEngine啓動時所必須的資源開闢內存)
                        self.audioEngine.prepare()
                        if (self.speechTask?.state == .running) {   // 若是當前進程狀態是進行中
                            // 中止語音識別
                           self.stopDictating()
                        } else {   // 進程狀態不在進行中
                            self.speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: languge))
                            guard (self.speechRecognizer != nil) else {
                                self.showAlert("抱歉,暫不支持當前地區使用語音輸入")
                                return
                            }
                            self.setCallBack(type: .start, text: nil)
                            // 開啓語音識別
                            self.startDictating()
                        }
                    } else {
                        self.showAlert("您已取消受權使用語音識別,若是須要使用語音識別功能,能夠到設置中從新開啓!")
                        self.setCallBack(type: .authDenied, text: nil)
                    }
                })
            } else {
                //麥克風沒有受權
                self.showAlert("您已取消受權使用麥克風,若是須要使用語音識別功能,能夠到設置中從新開啓!")
                self.setCallBack(type: .authDenied, text: nil)
            }
        }
    }
}


@available(iOS 10.0, *)
extension JPSpeechRecognition: SFSpeechRecognitionTaskDelegate {
    
    //判斷語音識別權限
    private func checkRecognizerAuthorization(recongStatus: @escaping (_ resType: Bool) -> Void) {
        let authorStatus = SFSpeechRecognizer.authorizationStatus()
        if authorStatus == .authorized {
            recongStatus(true)
        } else if authorStatus == .notDetermined {
            SFSpeechRecognizer.requestAuthorization { (status) in
                if status == .authorized {
                    recongStatus(true)
                } else {
                    recongStatus(false )
                }
            }
        } else {
            recongStatus(false)
        }
    }
    
    //檢測麥克風
    private func checkmicroPhoneAuthorization(authoStatus: @escaping (_ resultStatus: Bool) -> Void) {
        let microPhoneStatus = AVCaptureDevice.authorizationStatus(for: .audio)

        if microPhoneStatus == .authorized {
            authoStatus(true)
        } else if microPhoneStatus == .notDetermined {
            AVCaptureDevice.requestAccess(for: .audio, completionHandler: {(res) in
                if res {
                    authoStatus(true)
                } else {
                    authoStatus(false)
                }
            })
        } else {
            authoStatus(false)
        }
    }
    
    //開始進行
    private func startDictating() {
        do {
            recognitionRequest = SFSpeechAudioBufferRecognitionRequest()   // recreates recognitionRequest object.
            guard let recognitionRequest = recognitionRequest else {
                fatalError("Unable to created a SFSpeechAudioBufferRecognitionRequest object")
            }
        //啓用離線識別的開關,這個屬性只有IOS13以上才支持
        // Keep speech recognition data on device
        if #available(iOS 13, *) {
            recognitionRequest.requiresOnDeviceRecognition = true
        }
            try audioEngine.start()
            speechTask = speechRecognizer!.recognitionTask(with: recognitionRequest) { (speechResult, error) in
                // 識別結果,識別後的操做
                if speechResult == nil {
                    return
                }
                self.setCallBack(type: .finished, text: speechResult!.bestTranscription.formattedString)
            }
        } catch  {
            print(error)
            self.setCallBack(type: .finished, text: nil)
        }
    }
    
    // 中止聲音處理器,中止語音識別請求進程
    func stopDictating() {
        setCallBack(type: .stop, text: nil)
        
        audioEngine.stop()
        recognitionRequest?.endAudio()
        recognitionRequest = nil
        
        if audioEngine.inputNode.numberOfInputs > 0 {
            audioEngine.inputNode.removeTap(onBus: 0)
        }
        speechTask?.cancel()
    }
    
    private func setCallBack(type: JPSpeechType, text: String?) {
        if block != nil {
            block!(type, text)
        }
    }
    
    private func setAudioActive() {
        let audioSession = AVAudioSession.sharedInstance()
                       
       do {
           
           try audioSession.setCategory(AVAudioSession.Category.playAndRecord,mode: .default)
           try audioSession.setMode(AVAudioSession.Mode.spokenAudio)
           
           try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
           try audioSession.overrideOutputAudioPort(AVAudioSession.PortOverride.speaker)
           
       } catch  {
           debugPrint("Audio session initialization error: \(error.localizedDescription)")
       }
    }
    
    private func showAlert(_ message: String) {
// let alertVC = UIAlertController(title: nil, message: message, preferredStyle: .alert)
// let firstAction = UIAlertAction(title: "知道了", style: .default, handler: {(action) in
// })
// alertVC.addAction(firstAction)
// parentVc.present(alertVC, animated: true, completion: nil)
        JMProgressHUD.showInfo(message)
    }
}

複製代碼

方案二:百度語音識別

百度語音識別簡介

百度語音識別提供了不少功能,這裏我簡單介紹一下語音識別這塊的。 百度語音識別有如下特色:

  • 在線語音識別支持識別任意詞,離線語音識別僅支持命令詞識別(語法模式)
  • 首次使用離線,SDK將會後臺下載離線受權文件,成功後,受權文件有效期(三年)內無需聯網。有效期即將結束後SDK將自動屢次嘗試聯網更新證書)。
  • 沒有純離線識別。只能離線識別固定短語
  • 離線識別目前不支持任意語句。您能夠預先定義好,下載bsg文件 yuyin.baidu.com/asr bds_easr_gramm.dat 文件件的內容替換成 本身定義的bsg 文件的內容 自定義短語越多效果越差,建議不超過100行

百度語音識別SDK集成步驟

一。首先在百度語音開放平臺註冊,建立應用,生成API_KEY,SECRET_KEY和APP_ID 建立應用時的包名填工程的 Bundle identifier。百度語音識別註冊地址點擊這裏

百度語音識別控制檯

二,下載SDK,先打開官方demo運行看看, 替換建立應用生成的API_KEY,SECRET_KEY和APP_ID

百度語音識別IOS sdk
建立本身的appKey

建立本身的appKey
而後能夠先測試一下下載的百度官方demo,運行是否OK。

配置離線識別引擎
三,而後在本身項目集成,開發。

注意集成的資源包導入:

注意集成的資源包導入

例如在上面提供的離線SDK中,我只用一個簡單的類對百度SDK包裝了一層,離線配置方法以下:

離線模式配置

百度SDK 集成步驟以下:

  1. 將官方SDK中的以下文件拖入到本身的項目中:

    拖入SDK中必要的文件

  2. 添加必需要的系統框架,動態庫:

    添加必需要的系統框架,動態庫

  3. 封裝本身的類,實現對百度API調用:

  • 導入語音識別須要的頭文件:
#import "BDSASRDefines.h"
#import "BDSASRParameters.h"
#import "BDSEventManager.h"
複製代碼
  • 定義跟AppID綁定的 APP_ID, API_KEY, SECRET_KEY 相關信息,這個信息就是你在百度平臺註冊獲得的
const NSString* APP_ID = @"18569855";
const NSString* API_KEY = @"2qrMX1TgfTGslRMd3TcDuuBq";
const NSString* SECRET_KEY = @"xatUjET5NLNDXYNghNCnejt28MGpRYP2";
複製代碼
  • 初始化SDK,構建一個BDSEventManager對象,設置須要使用短語音服務:百度短語音productId = "1537"
- (instancetype)initWithLanguage:(KLanguage)language offlineGrammarDATFileURL:(NSURL * _Nullable)datFileURL {
    if (self = [super initWithLanguage:language]) {
        _offlineGrammarDATFileURL = [datFileURL copy];
        _asrEventManager = [BDSEventManager createEventManagerWithName:BDS_ASR_NAME];
        NSString *productId = [KHelper identifierForBaiduLanguage:language];
        [_asrEventManager setParameter:productId forKey:BDS_ASR_PRODUCT_ID];
    }
    return self;
}
複製代碼
  • 配置離線引擎和相關模型資源文件
- (void)configOfflineMode {
    [self.asrEventManager setDelegate:self];
    [self.asrEventManager setParameter:@(EVRDebugLogLevelError) forKey:BDS_ASR_DEBUG_LOG_LEVEL];
    
    
    // 參數配置:在線身份驗證
    [self.asrEventManager setParameter:@[API_KEY, SECRET_KEY] forKey:BDS_ASR_API_SECRET_KEYS];
    
    NSBundle *bundle = [NSBundle bundleForClass:[KBaiduRecognizer class]]; NSString *basicModelPath = [bundle pathForResource:@"bds_easr_basic_model" ofType:@"dat"]; [self.asrEventManager setParameter:basicModelPath forKey:BDS_ASR_MODEL_VAD_DAT_FILE]; [self.asrEventManager setParameter:@(YES) forKey:BDS_ASR_ENABLE_MODEL_VAD]; [self.asrEventManager setParameter:APP_ID forKey:BDS_ASR_OFFLINE_APP_CODE]; [self.asrEventManager setParameter:@(EVR_STRATEGY_BOTH) forKey:BDS_ASR_STRATEGY]; [self.asrEventManager setParameter:@(EVR_OFFLINE_ENGINE_GRAMMER) forKey:BDS_ASR_OFFLINE_ENGINE_TYPE]; [self.asrEventManager setParameter:basicModelPath forKey:BDS_ASR_OFFLINE_ENGINE_DAT_FILE_PATH]; // 離線僅可識別自定義語法規則下的詞 NSString *grammarFilePath = [[NSBundle mainBundle] pathForResource:@"baidu_speech_grammar" ofType:@"bsg"]; if (_offlineGrammarDATFileURL != nil) {
        if (![[NSFileManager defaultManager] fileExistsAtPath:_offlineGrammarDATFileURL.path]) {
            NSLog(@"!!! Error: 你提供的離線語法詞庫不存在: %@", _offlineGrammarDATFileURL.path);
        } else {
            grammarFilePath = _offlineGrammarDATFileURL.path;
        }
    }
    
    [self.asrEventManager setParameter:grammarFilePath forKey:BDS_ASR_OFFLINE_ENGINE_GRAMMER_FILE_PATH];
}
複製代碼

除了上面離線識別須要的簡單設備外,還能夠設置以下信息:

  1. 識別語言 @0 : @"普通話", @1 : @"粵語", @2 : @"英文", @3 : @"四川話"
//識別語言 @0 : @"普通話", @1 : @"粵語", @2 : @"英文", @3 : @"四川話"
    [self.asrEventManager setParameter:@(EVoiceRecognitionLanguageChinese) forKey:BDS_ASR_LANGUAGE];
複製代碼
  1. 採樣率 @"自適應", @"8K", @"16K"
//採樣率 @"自適應", @"8K", @"16K"
    [self.asrEventManager setParameter:@(EVoiceRecognitionRecordSampleRateAuto) forKey:BDS_ASR_SAMPLE_RATE];
複製代碼
  1. 是否啓用長語音識別
//是否啓用長語音識別
    [self.asrEventManager setParameter:@(YES) forKey:BDS_ASR_ENABLE_LONG_SPEECH];
      //開啓提示音 @0 : @"關閉", @(EVRPlayToneAll) : @"開啓"}
    //使用長語音必須關閉提示音
    [self.asrEventManager setParameter:@(0) forKey:BDS_ASR_PLAY_TONE];
  //開啓端點檢測 {@NO : @"關閉", @YES : @"開啓"} 使用長語音必須開啓本地VAD
    //端點檢測,即自動檢測音頻輸入的起始點和結束點。SDK默認開啓VAD,檢測到靜音後自動中止識別。
    //若是須要自行控制識別結束需關閉VAD,請同時關閉服務端VAD與端上VAD
    //[self.asrEventManager setParameter:@(YES) forKey:BDS_ASR_ENABLE_LOCAL_VAD];
   // 關閉服務端VAD
    [self.asrEventManager setParameter:@(NO) forKey:BDS_ASR_ENABLE_EARLY_RETURN];
    // 關閉本地VAD
    [self.asrEventManager setParameter:@(NO) forKey:BDS_ASR_ENABLE_LOCAL_VAD];    
    //打開的話配置端點檢測(二選一)
複製代碼
  • 配置 ModelVAD端點檢測方式 檢測更加精準,抗噪能力強,響應速度較慢
- (void)configModelVAD {
    NSString *modelVAD_filepath = [[NSBundle mainBundle] pathForResource:@"bds_easr_basic_model" ofType:@"dat"];
    //ModelVAD所需資源文件路徑
    [self.asrEventManager setParameter:modelVAD_filepath forKey:BDS_ASR_MODEL_VAD_DAT_FILE];
}
複製代碼
  • DNNMFE端點檢測方式 提供基礎檢測功能,性能高,響應速度快
//DNNMFE端點檢測方式 提供基礎檢測功能,性能高,響應速度快
- (void)configDNNMFE {
    //設置MFE模型文件
    NSString *mfe_dnn_filepath = [[NSBundle mainBundle] pathForResource:@"bds_easr_mfe_dnn" ofType:@"dat"];
    [self.asrEventManager setParameter:mfe_dnn_filepath forKey:BDS_ASR_MFE_DNN_DAT_FILE];
    //設置MFE CMVN文件路徑
    NSString *cmvn_dnn_filepath = [[NSBundle mainBundle] pathForResource:@"bds_easr_mfe_cmvn" ofType:@"dat"];
    [self.asrEventManager setParameter:cmvn_dnn_filepath forKey:BDS_ASR_MFE_CMVN_DAT_FILE]    
    //是否使用ModelVAD,打開需配置資源文件參數
    [self.asrEventManager setParameter:@(NO) forKey:BDS_ASR_ENABLE_MODEL_VAD];
    // MFE支持自定義靜音時長
    // [self.asrEventManager setParameter:@(500.f) forKey:BDS_ASR_MFE_MAX_SPEECH_PAUSE];
    // [self.asrEventManager setParameter:@(500.f) forKey:BDS_ASR_MFE_MAX_WAIT_DURATION];
}
複製代碼
  • 離在線並行配置
// 參數設置:識別策略爲離在線並行
        [self.asrEventManager setParameter:@(EVR_STRATEGY_BOTH) forKey:BDS_ASR_STRATEGY];
        // 參數設置:離線識別引擎類型 EVR_OFFLINE_ENGINE_INPUT 輸入法模式 EVR_OFFLINE_ENGINE_GRAMMER 離線引 擎語法模式
        //離線語音識別僅支持命令詞識別(語法模式)。
        //[self.asrEventManager setParameter:@(EVR_OFFLINE_ENGINE_INPUT) forKey:BDS_ASR_OFFLINE_ENGINE_TYPE];
        [self.asrEventManager setParameter:@(EVR_OFFLINE_ENGINE_GRAMMER) forKey:BDS_ASR_OFFLINE_ENGINE_TYPE];
        //並生成bsg文件。下載語法文件後,設置BDS_ASR_OFFLINE_ENGINE_GRAMMER_FILE_PATH參數
        NSString* gramm_filepath = [[NSBundle mainBundle] pathForResource:@"bds_easr_gramm" ofType:@"dat"];
        // 請在 (官網)[http://speech.baidu.com/asr] 參考模板定義語法,下載語法文件後,替換BDS_ASR_OFFLINE_ENGINE_GRAMMER_FILE_PATH參數
        [self.asrEventManager setParameter:gramm_filepath forKey:BDS_ASR_OFFLINE_ENGINE_GRAMMER_FILE_PATH];
        //離線識別資源文件路徑
        NSString* lm_filepath = [[NSBundle mainBundle] pathForResource:@"bds_easr_basic_model" ofType:@"dat"];
        [self.asrEventManager setParameter:lm_filepath forKey:BDS_ASR_OFFLINE_ENGINE_DAT_FILE_PATH];
         //加載離線引擎
        [self.asrEventManager sendCommand:BDS_ASR_CMD_LOAD_ENGINE];   
複製代碼
  • 監聽回調代理
#pragma mark -- 語音識別狀態、錄音數據等回調均在此代理中發生
- (void)VoiceRecognitionClientWorkStatus:(int)workStatus obj:(id)aObj{
    switch (workStatus) {
        case EVoiceRecognitionClientWorkStatusNewRecordData: {
            [self.fileHandler writeData:(NSData *)aObj];
            NSLog(@"錄音數據回調");
            break;
        }
            
        case EVoiceRecognitionClientWorkStatusStartWorkIng: {
            NSLog(@"識別工做開始開始採集及處理數據");
            NSDictionary *logDic = [self parseLogToDic:aObj];
            [self printLogTextView:[NSString stringWithFormat:@"開始識別-log: %@\n", logDic]];
            break;
        }
        case EVoiceRecognitionClientWorkStatusStart: {
            NSLog(@"檢測到用戶開始說話");
            [self printLogTextView:@"檢測到用戶開始說話.\n"];
            break;
        }
        case EVoiceRecognitionClientWorkStatusEnd: {
            NSLog(@"用戶說話完成,但服務器還沒有返回結果");
            [self printLogTextView:@"用戶說話完成,但服務器還沒有返回結果.\n"];
            self.contentTextView.text = @"無識別結果";
            break;
        }
        case EVoiceRecognitionClientWorkStatusFlushData: {
            // 逐句顯示。配合連續上屏的中間結果,能夠進一步 升語音輸入的體驗
            //// 該狀態值表示服務器返回了中間結果,若是想要將中間結果展現給用戶(造成連續上屏的效果),
            // 能夠利用與該狀態同時返回的數據,每當接到新的該類消息應當清空顯示區域的文字以避免重複
            NSLog(@"逐句顯示");
            [self printLogTextView:[NSString stringWithFormat:@"服務器返回了中間結 - %@.\n\n", [self getDescriptionForDic:aObj]]];
            
            self.contentTextView.text = @"";
            NSArray *contentArr = aObj[@"results_recognition"];
            NSString *contentStr = contentArr[0];
            self.contentTextView.text = contentStr;
            
            break;
        }
        case EVoiceRecognitionClientWorkStatusFinish: {
            //// 該狀態值表示語音識別服務器返回了最終結果,結果以數組的形式保存在 aObj 對象中
            // 接受到該消息時應當清空顯示區域的文字以避免重複
            NSLog(@"返回了最終結果");
            /* "origin_result" = { "corpus_no" = 6643061564690340286; "err_no" = 0; result = { word = ( "\U597d\U7684" ); }; sn = "5EEAC770-DDD2-4D35-8ABF-F407276A7934"; "voice_energy" = "29160.45703125"; }; "results_recognition" = ( "\U597d\U7684" ); */
            
            [self printLogTextView:[NSString stringWithFormat:@"最終結果 - %@.\n", [self getDescriptionForDic:aObj]]];
            if (aObj) {
                
                // NSArray *contentArr = aObj[@"results_recognition"];
                // NSString *contentStr = contentArr[0];
                // NSLog(@"contentStr = %@",contentStr);
                self.contentTextView.text =  [self getDescriptionForDic:aObj];
                
            }
            
            break;
        }
        case EVoiceRecognitionClientWorkStatusMeterLevel: {
            NSLog(@"當前音量回調");
            break;
        }
        case EVoiceRecognitionClientWorkStatusCancel: {
            NSLog(@"用戶主動取消");
            [self printLogTextView:@"用戶主動取消.\n"];
            
            break;
        }
        case EVoiceRecognitionClientWorkStatusError: {
            // 錯誤狀態 沒有語音輸入
            NSLog(@"錯誤狀態");
            NSError * error = (NSError *)aObj;
            
            if (error.code == 2228236) {
                ////離線引擎錯誤狀態:
                //識別失敗,沒法識別。(語法模式下,可能爲語音不在自定義的語法規則之下)
                 [self printLogTextView:[NSString stringWithFormat:@"錯誤狀態 -語法模式下,可能爲語音不在自定義的語法規則之下\n %@.\n", (NSError *)aObj]];
            }else if (error.code == 2228230){
                 [self printLogTextView:[NSString stringWithFormat:@"錯誤狀態 -dat模型文件不可用,請設置 BDS_ASR_OFFLINE_ENGINE_DAT_FILE_PATH\n %@.\n", (NSError *)aObj]];
            }else if (error.code == 2228231){
                 [self printLogTextView:[NSString stringWithFormat:@"錯誤狀態 -grammar文件無效,請設置 BDS_ASR_OFFLINE_ENGINE_GRAMMER_FILE_PATH\n %@.\n", (NSError *)aObj]];
            }else if (error.code == 2225219){
                [self printLogTextView:[NSString stringWithFormat:@"錯誤狀態 -音頻質量太低,沒法識別\n %@.\n", (NSError *)aObj]];
            }else{
                [self printLogTextView:[NSString stringWithFormat:@"錯誤狀態 - %@.\n", (NSError *)aObj]];
            }
           
            break;
        }
        case EVoiceRecognitionClientWorkStatusLoaded: {
            NSLog(@"離線引擎加載完成");
            [self printLogTextView:@"離線引擎加載完成.\n"];
            break;
        }
        case EVoiceRecognitionClientWorkStatusUnLoaded: {
            NSLog(@"離線引擎卸載完成");
            [self printLogTextView:@"離線引擎卸載完成.\n"];
            break;
        }
        case EVoiceRecognitionClientWorkStatusChunkThirdData: {
            NSLog(@"識別結果中的第三方數據");
            [self printLogTextView:[NSString stringWithFormat:@"識別結果中的第三方數據: %lu\n", (unsigned long)[(NSData *)aObj length]]];
            break;
        }
        case EVoiceRecognitionClientWorkStatusChunkNlu: {
            NSLog(@"別結果中的語義結果");
            NSString *nlu = [[NSString alloc] initWithData:(NSData *)aObj encoding:NSUTF8StringEncoding];
            [self printLogTextView:[NSString stringWithFormat:@"識別結果中的語義結果: %@\n", nlu]];
            NSLog(@"%@", nlu);
            break;
        }
        case EVoiceRecognitionClientWorkStatusChunkEnd: {
            NSLog(@"識別過程結束");
            [self printLogTextView:[NSString stringWithFormat:@"識別過程結束, sn: %@.\n", aObj]];
            
            break;
        }
        case EVoiceRecognitionClientWorkStatusFeedback: {
            NSLog(@"識別過程反饋的打點數據");
            NSDictionary *logDic = [self parseLogToDic:aObj];
            [self printLogTextView:[NSString stringWithFormat:@"識別過程反饋的打點數據: %@\n", logDic]];
            break;
        }
        case EVoiceRecognitionClientWorkStatusRecorderEnd: {
            //錄音機關閉,頁面跳轉需檢測此時間,規避狀態條 (iOS)
            NSLog(@"錄音機關閉");
            [self printLogTextView:@"錄音機關閉.\n"];
            break;
        }
        case EVoiceRecognitionClientWorkStatusLongSpeechEnd: {
            NSLog(@"長語音結束狀態");
            [self printLogTextView:@"長語音結束狀態.\n"];
            
            break;
        }
        default:
            break;
    }
    
}
複製代碼
  • 提供開始,中止識別方法
//開始識別
- (void) startWithResultHandler:(KRecognitionResultHandler)resultHandler errorHandler:(KErrorHandler)errorHandler {
    self.resultHandler = resultHandler;
    self.errorHandler = errorHandler;
    [self.asrEventManager sendCommand:BDS_ASR_CMD_START];
}

//中止識別
- (void)stop {
    [self.asrEventManager sendCommand:BDS_ASR_CMD_STOP];
}

複製代碼
  • 完整封裝代碼以下:
//
// KBaiduRecognizer.m
// KSpeechRecognition
//
// Created by yulu kong on 2020/4/3.
// Copyright © 2020 yulu kong. All rights reserved.
//

#import "KBaiduRecognizer.h"
#import "BDSASRDefines.h"
#import "BDSASRParameters.h"
#import "BDSEventManager.h"
#import "KHelper.h"
#import "KError.h"

const NSString* APP_ID = @"18569855";
const NSString* API_KEY = @"2qrMX1TgfTGslRMd3TcDuuBq";
const NSString* SECRET_KEY = @"xatUjET5NLNDXYNghNCnejt28MGpRYP2";

@interface KBaiduRecognizer () <BDSClientASRDelegate>
@property (strong, nonatomic) BDSEventManager *asrEventManager;
@property (nonatomic, strong) NSURL *offlineGrammarDATFileURL;

@end

@implementation KBaiduRecognizer


- (KAuthorizationStatus)authorizationStatus {
    return KAuthorizationStatusAuthorized;
}

- (instancetype)initWithLanguage:(KLanguage)language {
    return [self initWithLanguage:language offlineGrammarDATFileURL:nil];
}

- (instancetype)initWithLanguage:(KLanguage)language offlineGrammarDATFileURL:(NSURL * _Nullable)datFileURL {
    if (self = [super initWithLanguage:language]) {
        _offlineGrammarDATFileURL = [datFileURL copy];
        // 建立語音識別對象
        _asrEventManager = [BDSEventManager createEventManagerWithName:BDS_ASR_NAME];
        NSString *productId = [KHelper identifierForBaiduLanguage:language];
        [_asrEventManager setParameter:productId forKey:BDS_ASR_PRODUCT_ID];
    }
    return self;
}

- (void) startWithResultHandler:(KRecognitionResultHandler)resultHandler errorHandler:(KErrorHandler)errorHandler {
    self.resultHandler = resultHandler;
    self.errorHandler = errorHandler;
    [self.asrEventManager sendCommand:BDS_ASR_CMD_START];
}

- (void)stop {
    [self.asrEventManager sendCommand:BDS_ASR_CMD_STOP];
}

- (void)configOfflineMode {
    // 設置語音識別代理
    [self.asrEventManager setDelegate:self];
    [self.asrEventManager setParameter:@(EVRDebugLogLevelError) forKey:BDS_ASR_DEBUG_LOG_LEVEL];
    
    
    // 參數配置:在線身份驗證
    [self.asrEventManager setParameter:@[API_KEY, SECRET_KEY] forKey:BDS_ASR_API_SECRET_KEYS];
    
    NSBundle *bundle = [NSBundle bundleForClass:[KBaiduRecognizer class]]; NSString *basicModelPath = [bundle pathForResource:@"bds_easr_basic_model" ofType:@"dat"]; [self.asrEventManager setParameter:basicModelPath forKey:BDS_ASR_MODEL_VAD_DAT_FILE]; [self.asrEventManager setParameter:@(YES) forKey:BDS_ASR_ENABLE_MODEL_VAD]; //離線引擎身份驗證 設置 APPID 離線受權所需APPCODEAPPID),如使用該方式進行正式受權,請移除臨時受權文件 [self.asrEventManager setParameter:APP_ID forKey:BDS_ASR_OFFLINE_APP_CODE]; //識別策略 @0 : @"在線識別", @4 : @"離在線並行" [self.asrEventManager setParameter:@(EVR_STRATEGY_BOTH) forKey:BDS_ASR_STRATEGY]; [self.asrEventManager setParameter:@(EVR_OFFLINE_ENGINE_GRAMMER) forKey:BDS_ASR_OFFLINE_ENGINE_TYPE]; [self.asrEventManager setParameter:basicModelPath forKey:BDS_ASR_OFFLINE_ENGINE_DAT_FILE_PATH]; // 離線僅可識別自定義語法規則下的詞 NSString *grammarFilePath = [[NSBundle mainBundle] pathForResource:@"baidu_speech_grammar" ofType:@"bsg"]; if (_offlineGrammarDATFileURL != nil) {
        if (![[NSFileManager defaultManager] fileExistsAtPath:_offlineGrammarDATFileURL.path]) {
            NSLog(@"!!! Error: 你提供的離線語法詞庫不存在: %@", _offlineGrammarDATFileURL.path);
        } else {
            grammarFilePath = _offlineGrammarDATFileURL.path;
        }
    }
    
    [self.asrEventManager setParameter:grammarFilePath forKey:BDS_ASR_OFFLINE_ENGINE_GRAMMER_FILE_PATH];
}


// MARK: - BDSClientASRDelegate

- (void)VoiceRecognitionClientWorkStatus:(int)workStatus obj:(id)aObj {
    switch (workStatus) {
        case EVoiceRecognitionClientWorkStatusStartWorkIng: {
            break;
        }
        case EVoiceRecognitionClientWorkStatusStart:
            break;
            
        case EVoiceRecognitionClientWorkStatusEnd: {
            break;
        }
        case EVoiceRecognitionClientWorkStatusFlushData: {
            [self receiveRecognitionResult:aObj isFinal:NO];
            break;
        }
        case EVoiceRecognitionClientWorkStatusFinish: {
            [self receiveRecognitionResult:aObj isFinal:YES];
            break;
        }
        case EVoiceRecognitionClientWorkStatusError: {
            self.errorHandler([KError errorWithCode:-1 message:@"語音識別失敗"]);
            break;
        }
        case EVoiceRecognitionClientWorkStatusRecorderEnd: {
            break;
        }
        default:
            break;
    }
}

- (void)receiveRecognitionResult:(id)resultInfo isFinal:(BOOL)isFinal {
    if (resultInfo == nil || ![resultInfo isKindOfClass:[NSDictionary class]]) {
        return;
    }
    
    NSDictionary *info = (NSDictionary *)resultInfo;
    NSString *text = info[@"results_recognition"];
    if (text != nil && [text length] > 0) {
        self.resultHandler([[KRecognitionResult alloc] initWithText:text isFinal:isFinal]);
    }
}

@end


複製代碼

方案三:使用開源框架

1. KALDI

kaldi源碼下載:kaldi源碼下載 安裝git後運行

git clone https://github.com/kaldi-asr/kaldi.git kaldi-trunk --origin golden
複製代碼

速度過慢能夠參考:github下載提速

經過http://git.oschina.net/離線下載的方式

git clone https://gitee.com/cocoon_zz/kaldi.git kaldi-trunk --origin golden
複製代碼

KALDI簡介

KALDI 簡介

KALDI是著名的開源自動語音識別(ASR)工具,這套工具提供了搭建目前工業界最經常使用的ASR模型的訓練工具,同時也提供了其餘一些子任務例如說話人驗證(speaker verification)和語種識別(language recognition)的pipeline。KALDI目前由Daniel Povey維護,他以前在劍橋作ASR相關的研究,後來去了JHU開發KALDI,目前在北京小米總部做爲語音的負責人。同時他也是另外一個著名的ASR工具HTK的主要做者之一。 KALDI之因此在ASR領域如此流行,是由於該工具提供了其餘ASR工具不具有的能夠在工業中使用的神經網絡模型(DNN,TDNN,LSTM)。可是與其餘基於Python接口的通用神經網絡庫(TensorFlow,PyTorch等)相比,KALDI提供的接口是一系列的命令行工具,這就須要學習者和使用者須要比較強的shell腳本能力。同時,KALDI爲了簡化搭建語音識別pipeline的過程,提供了大量的通用處理腳本,這些腳本主要是用shell,perl以及python寫的,這裏主要須要讀懂shell和python腳本,perl腳本主要是一些文本處理工做,而且能夠被python取代,所以學習的性價比並不高。整個KALDI工具箱的結構以下圖所示。

KALDI工具箱的結構
能夠看到KALDI的矩陣計算功能以及構圖解碼功能分別是基於BLAS/LAPACK/MKL和OpenFST這兩個開源工具庫的,在這些庫的基礎上,KALDI實現了Matrix,Utils,Feat,GMM,SGMM,Transforms,LM,Tree,FST ext,HMM,Decoder等工具,經過編譯以後能夠生成命令行工具,也就是圖中的C++ Executables文件。最後KALDI提供的樣例以及通用腳本展現瞭如何使用這些工具來搭建ASR Pipeline,是很好的學習材料。 除了KALDI自己提供的腳本,還有其官方網站的Documents也是很好的學習資料。固然,除了KALDI工具自己,ASR的原理也須要去學習,只有左右開弓,才能更好的掌握這個難度較高的領域。

KALDI 源碼編譯 安裝

如何安裝參考下載好的目錄內INSTALL文件

This is the official Kaldi INSTALL. Look also at INSTALL.md for the git mirror installation.
[for native Windows install, see windows/INSTALL]

(1)
go to tools/  and follow INSTALL instructions there.

(2)
go to src/ and follow INSTALL instructions there.
複製代碼

出現問題首先查看各個目錄下面的INSTALL文件,有些配置的問題(例如gcc的版本以及CUDA等)均可以查看該文檔進行解決。

依賴文件編譯

首先檢查依賴項

cd extras
sudo bash check_dependencies.sh
複製代碼

注意make -j4能夠多進程進行

cd kaldi-trunk/tools
make
複製代碼

配置Kaldi源碼

cd ../src
#若是沒有GPU請查閱configure,設置不使用CUDA
./configure --shared
複製代碼

編譯Kaldi源碼

make all
#注意在這裏有可能關於CUDA的編譯不成功,緣由是configure腳本沒有正確的找出CUDA的位置,須要手動編輯configure查找的路徑,修改CUDA庫的位置
複製代碼

測試安裝是否成功

cd ../egs/yesno/s5
./run.sh
複製代碼

安裝成功效果圖

解碼的結果放置在exp目錄(export)下,例如咱們查看一下 ~/kaldi-bak/egs/yesno/s5/exp/mono0a/log$ vim align.38.1.log

yesno結果
這裏面0,1就分別表明說的是no仍是yes啦。

語音識別原理相關資料

語音識別的原理 www.zhihu.com/question/20…

HTK Book www.ee.columbia.edu/ln/LabROSA/…

如何用簡單易懂的例子解釋隱馬爾可夫模型? www.zhihu.com/question/20…

kaldi一些文件解讀

1:run.sh  總的運行文件,裏面把其餘運行文件都集成了。 { 執行順序:run.sh >>> path.sh >>> directory(存放訓練數據的目錄) >>> mono-phone>>>triphone>>>lda_mllt>>>sat>>>quitck data preparation:

1:generate text,wav.scp,utt2spk,spk2utt (將數據生成這些文件) (由local/data_prep.sh生成) text:包含每段發音的標註  sw02001-A_002736-002893 AND IS wav.scp: (extended-filename:實際的文件名) sw02001-A /home/dpovey/kaldi-trunk/tools/sph2pipe_v2.5/sph2pipe -f wav -p -c 1 /export/corpora3/LDC/LDC97S62/swb1/sw02001.sph | utt2spk:  指明某段發音是哪一個人說的(注意一點,說話人編號並不須要與說話人實際的名字徹底一致——只須要大概可以猜出來就行。) sw02001-A_000098-001156 2001-A spk2utt: ... (utt2spk和spk2utt文件中包含的信息是同樣的) 2:produce MFCC features 3:prepare language stuff(build a large lexicon that invovles words in both the training and decoding.) 4:monophone單音素訓練 5:tri1三音素訓練(以單音素模型爲輸入訓練上下文相關的三音素模型), trib2進行lda_mllt聲學特徵變換,trib3進行sat天然語言適應(運用基於特徵空間的最大似然線性迴歸(fMLLR)進行說話人自適應訓練),trib4作quick LDA-MLLT(Linear Discriminant Analysis – Maximum Likelihood Linear Transform), LDA根據降維特徵向量創建HMM狀態。MLLT根據LDA降維後的特徵空間得到每個說話人的惟一變換。MLLT其實是說話人的歸一化。 SAT(Speaker Adaptive Training)。SAT一樣對說話人和噪聲的歸一化。 5:DNN }

2:cmd.sh  通常須要修改

export train_cmd=run.pl #將原來的queue.pl改成run.pl export decode_cmd="run.pl"#將原來的queue.pl改成run.pl這裏的--mem 4G export mkgraph_cmd="run.pl"#將原來的queue.pl改成run.pl 這裏的--mem 8G export cuda_cmd="run.pl" #將原來的queue.pl改成run.pl 這裏去掉原來的--gpu 1(若是沒有gpu)

3:path.sh (設置環境變量)

export KALDI_ROOT=pwd/../../.. [ -f KALDI_ROOT/tools/env.sh ] && .KALDI_ROOT/tools/env.sh export PATH=PWD/utils/:KALDI_ROOT/tools/openfst/bin:PWD:PATH [ ! -f KALDI_ROOT/tools/config/common_path.sh ] && echo >&2 "The standard fileKALDI_ROOT/tools/config/common_path.sh is not present -> Exit!" && exit 1 . KALDI_ROOT/tools/config/common_path.sh
export LC_ALL=C
咱們看到是在運行run.sh是要用到的環境變量,在這裏先設置一下.
咱們看到先是設置了KALDI_ROOT,它實際就是kaldi的源碼的根目錄。
[ -fKALDI_ROOT/tools/env.sh ] && . $KALDI_ROOT/tools/env.sh 這句話的意思是若是存在這個環境變量腳本就執行這個腳本,可是我沒有在該路徑下發現這個腳本。 而後是將本目錄下的utils目錄, kaldi根目錄下的tools/openfst/bin目錄 和 本目錄加入到環境變量PATH中。 而後是判斷若是在kaldi根目錄下的tools/config/common_path.sh不存在,就打印提示缺乏該文件,而且退出。

Kaldi訓練腳本針對不一樣的語料庫,須要重寫數據準備部分,腳本通常放在conf、local文件夾裏; conf放置一些配置文件,如提取mfcc、filterbank等參數的配置,解碼時的參數配置 (主要是配置頻率,將系統採樣頻率與語料庫的採樣頻率設置爲一致) local通常用來放置處理語料庫的數據準備部分腳本 > 中文識別,應該準備:發音詞典、音頻文件對應的文本內容和一個基本可用的語言模型(解碼時使用) 數據訓練完後: exp目錄下: final.mdl 訓練出來的模型 graph_word目錄下: words.txt HCLG.fst 一個是字典,一個是有限狀態機(fst:發音字典,輸入是音素,輸出是詞)

kaldi編譯成iOS靜態庫

編譯腳本以下:

#!/bin/bash

if [ ! \( -x "./configure" \) ] ; then
    echo "This script must be run in the folder containing the \"configure\" script."
    exit 1
fi

export DEVROOT=`xcode-select --print-path`
export SDKROOT=$DEVROOT/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk
 # Set up relevant environment variables
export CPPFLAGS="-I$SDKROOT/usr/include/c++/4.2.1/ -I$SDKROOT/usr/include/ -miphoneos-version-min=10.0 -arch arm64"
export CFLAGS="$CPPFLAGS -arch arm64 -pipe -no-cpp-precomp -isysroot $SDKROOT"
#export CXXFLAGS="$CFLAGS"
export CXXFLAGS="$CFLAGS  -std=c++11 -stdlib=libc++"

MODULES="online2 ivector nnet2 lat decoder feat transform gmm hmm tree matrix util base itf cudamatrix fstext"
INCLUDE_DIR=include/kaldi
mkdir -p $INCLUDE_DIR

echo "Copying include files"
LIBS=""
for m in $MODULES
do
  cd $m
  echo
  echo "BUILDING MODULE $m"
  echo
  if [[ -f Makefile ]]
  then
    make
    lib=$(ls *.a)  # this will fail (gracefully) for ift module since it only contains .h files
    LIBS+=" $m/$lib"
  fi

  echo "建立模塊文件夾:$INCLUDE_DIR/$m"
  cd ..
  mkdir -p $INCLUDE_DIR/$m
  echo "拷貝文件到:$INCLUDE_DIR/$m/"
  cp -v $m/*h $INCLUDE_DIR/$m/
done

echo "LIBS: $LIBS"

LIBNAME="kaldi-ios.a"
libtool -static -o $LIBNAME $LIBS

cat >&2 << EOF

Build succeeded! 

Library is in $LIBNAME
h files are in $INCLUDE_DIR

EOF

複製代碼

上面這個腳本只編譯了支持arm64架構的靜態庫,在真機環境下測試,想支持其餘的架構的,能夠直接添加:

export CPPFLAGS="-I$SDKROOT/usr/include/c++/4.2.1/ -I$SDKROOT/usr/include/ -miphoneos-version-min=10.0 -arch arm64"
複製代碼

上面的腳原本自大神:長風浮雲 他的簡書地址:www.jianshu.com/p/faff2cd48… 他寫了好多關於kaldi的相關博客,若是須要研究能夠參考他的博客。

基於kaldi 源碼編譯的IOS 在線,離線語音識別Demo

這裏引用他提供的IOS 在線和離線識別的兩個demo:

2. CMUSphinx

CMUSphinx 官方資源導航:

  1. 這款語音識別開源框架相比於Kaldi比較適合作開發,各類函數上的封裝淺顯易懂,解碼部分的代碼很是容易看懂,且除開PC平臺,做者也考慮到了嵌入式平臺,Android開發也很方便,已有對應的Demo,Wiki上有基於PocketSphinx的語音評測的例子,且實時性相比Kaldi好了不少。
  2. 因爲適合開發,有不少基於它的各類開源程序、教育評測論文。
  3. 總的來講,從PocketSphinx來入門語音識別是一個不錯的選擇。
  • 缺點

相比於Kaldi,使用的是GMM-HMM框架,準確率上可能會差一些;其餘雜項處理程序(如pitch提取等等)沒有Kaldi多。

Sphinx工具介紹

Pocketsphinx —用C語言編寫的輕量級識別庫,主要是進行識別的。

Sphinxbase — Pocketsphinx所須要的支持庫,主要完成的是語音信號的特徵提取;

Sphinx3 —爲語音識別研究用C語言編寫的解碼器

Sphinx4 —爲語音識別研究用JAVA語言編寫的解碼器

CMUclmtk —語言模型訓練工具

Sphinxtrain —聲學模型訓練工具

下載網址:sourceforge.net/projects/cm…

Sphinx是由美國卡內基梅隆大學開發的大詞彙量、非特定人、連續英語語音識別系統。Sphinx從開發之初就獲得了CMU、DARPA等多個部門的資助和支持,後來逐步發展爲開源項目。目前CMU Sphinx小組開發的下列譯碼器:

Sphinx-2採用半連續隱含馬爾可夫模型(SCHMM)建模,採用的技術相對落後,使得識別精度要低於其它的譯碼器。

PocketSphinx是一個計算量和體積都很小的嵌入式語音識別引擎。在Sphinx-2的基礎上針對嵌入式系統的需求修改、優化而來,是第一個開源面向嵌入式的中等詞彙量連續語音識別項目。識別精度和Sphinx-2差很少。

Sphinx-3是CMU高水平的大詞彙量語音識別系統,採用連續隱含馬爾可夫模型CHMM建模。支持多種模式操做,高精度模式扁平譯碼器,由Sphinx3的最第一版本優化而來;快速搜索模式樹譯碼器。目前將這兩種譯碼器融合在一塊兒使用。

Sphinx-4是由JAVA語言編寫的大詞彙量語音識別系統,採用連續的隱含馬爾可夫模型建模,和之前的版本相比,它在模塊化、靈活性和算法方面作了改進,採用新的搜索策略,支持各類不一樣的語法和語言模型、聽覺模型和特徵流,創新的算法容許多種信息源合併成一種更符合實際語義的優雅的知識規則。因爲徹底採用JAVA語言開發,具備高度的可移植性,容許多線程技術和高度靈活的多線程接口。

3.

參考: www.jianshu.com/p/0f4a53450…

相關文章
相關標籤/搜索