1. 程式人生 > >使用訊飛語音實現語音識別,朗讀文字

使用訊飛語音實現語音識別,朗讀文字

一.走近訊飛(iFly)

語音技術實現了人機語音互動,使人與機器之間溝通變得像人與人溝通一樣簡單。語音技術主要包括語音合成和語音識別兩項關鍵技術。讓機器說話,用的是語音合成技術;讓機器聽懂人說話,用的是語音識別技術。此外,語音技術還包括語音編碼、音色轉換、口語評測、語音消噪和增強等技術,有著廣闊應用空間。

  • 早期的語音識別技術讓人啼笑皆非, 就連Siri剛出道時, 也是漏洞百出. 但是訊飛通過多年的不懈努力, 最近發展迅速, 這也是技術型專案前期技術積累的必然結果.
  • 百度也推出了自己的語音識別, 但是因為技術積累尚淺, 移植和測試體驗尚不如訊飛 – 本條個人觀點.

科大訊飛從開始的只做語音識別和語音合成, 到現在的廣告+統計+廣場+人臉識別+聲紋識別+推送

, 可以看出它的野心–打造綜合性平臺, 同時又不放棄專營業務(並且擁有難以記憶的英文縮寫和logo).

從使用訊飛的SDK過程中, 還是能感覺到誠意的, 很多設計很人性化, 免費提供了諸多測試和使用介面, 讓人好感倍增, 這也是為啥我為其做了這麼多廣告.

二.搭建環境

登入開發者平臺
  • 註冊使用者並且登入
  • 建立新應用

    選擇建立新應用:

    file-listfile-list

    這裡可以比較隨意填寫, 但是注意平臺別搞錯.

    file-listfile-list
    應用建立好之後, 請記錄下訊飛為該APP生成的Appid: 56678310 (每個人都不一樣哦)

  • 為新應新增服務

    新建立的應用可以在”我的應用”中檢視, 開始的時候, 這個應用沒有使用任何SDK, 我們需要向訊飛註冊一下我們的app都需要哪些服務.

    file-listfile-list

    點選”開通更多服務”, 選擇語言聽寫和線上語音合成兩個SDK, 第一個開發語義是自己新增上的.

    file-listfile-list

  • 下載相應SDK

    進入下載SDK介面, 您可以通過諸多位置進入到這裡, 可能與截圖不符, 但沒有問題.

    file-listfile-list

    這裡選擇”組合服務SDK下載”, 勾選圖中前兩個.

    file-listfile-list

    選擇平臺

    file-listfile-list

    最後選擇剛才建立的引用, 之後點選下載.

    file-listfile-list

  • 新建xcode(singleView)工程, 將下載好的資料夾中lib下的iflyMac匯入(拖入)工程

  • 新增引用庫
    file-listfile-list

三.程式碼+++++

  • 在storyBoard的viewController中拖入幾個控制元件, 一個UILable用來顯示語音翻譯後的文字, 兩個UIbutton用來觸發”帶介面的實時翻譯”和”不帶介面的實時翻譯”. 併為他們拖出屬性和響應方法.
    如圖:
    file-listfile-list

  • appdelegate.m中, 新增如下程式碼(註冊):
    AppDelegate.m 的 didFinishLaunchingWithOptions中:

    1
    2
    3
    4
    5
    6
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        // 根據appid登入到訊飛的伺服器, 過程需要身份驗證 , 56678310
        NSString *initString = [[NSString alloc] initWithFormat:@"appid=%@",@"你的appid, 別用我的"];
        [IFlySpeechUtility createUtility:initString];
        return YES;
    }
    

下面是寫好後的ViewController程式碼:

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#import "ViewController.h"
#import <iflyMSC/iflyMSC.h>
// 這個標頭檔案是做什麼的呢?
#import "ISRDataHelper.h"
// 還有這個.
#import "IATConfig.h"


@interface ViewController ()<IFlyRecognizerViewDelegate, IFlySpeechRecognizerDelegate>
// 翻譯好的Text會展示在這個label上.
@property (weak, nonatomic) IBOutlet UILabel *textView;

/*!
 *  語音識別控制元件
 *    錄音時觸控控制元件結束錄音,開始識別(相當於舊版的停止);觸控其他位置,取消錄音,結束會話(取消)
 *  出錯時觸控控制元件,重新開啟會話(相當於舊版的再說一次);觸控其他位置,取消錄音,結束會話(取消)
 *
 */
@property (nonatomic,strong)IFlyRecognizerView * iflyRecognizerView;

/*!
 *  語音識別類
 *   此類現在設計為單例,你在使用中只需要建立此物件,不能呼叫release/dealloc函式去釋放此物件。所有關於語音識別的操作都在此類中。
 */
@property (nonatomic, strong)IFlySpeechRecognizer * iFlySpeechRecognizer;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // 語音識別檢視空間及配置.
    [self initRecognizerView];

    // 語音識別類的初始化及配置.
    [self initSpeechRecognizer];
}

// !!!:語音識別檢視空間及配置--方法.
-(void)initRecognizerView{
    _iflyRecognizerView = [[IFlyRecognizerView alloc] initWithCenter:self.view.center];
    _iflyRecognizerView.delegate = self;
    [_iflyRecognizerView setParameter: @"iat" forKey: [IFlySpeechConstant IFLY_DOMAIN]];
    //asr_audio_path保存錄音檔名,如不再需要,設定value為nil表示取消,預設目錄是documents
    [_iflyRecognizerView setParameter:@"asrview.pcm " forKey:[IFlySpeechConstant ASR_AUDIO_PATH]];
}
// !!!:語音識別類的初始化及配置--方法.
-(void)initSpeechRecognizer{
    //單例模式,無UI的例項
    if (_iFlySpeechRecognizer == nil) {
        _iFlySpeechRecognizer = [IFlySpeechRecognizer sharedInstance];

        [_iFlySpeechRecognizer setParameter:@"" forKey:[IFlySpeechConstant PARAMS]];

        //設定聽寫模式
        [_iFlySpeechRecognizer setParameter:@"iat" forKey:[IFlySpeechConstant IFLY_DOMAIN]];
    }
    _iFlySpeechRecognizer.delegate = self;

    if (_iFlySpeechRecognizer != nil) {
        IATConfig *instance = [IATConfig sharedInstance];

        //設定最長錄音時間
        [_iFlySpeechRecognizer setParameter:instance.speechTimeout forKey:[IFlySpeechConstant SPEECH_TIMEOUT]];
        //設定後端點
        [_iFlySpeechRecognizer setParameter:instance.vadEos forKey:[IFlySpeechConstant VAD_EOS]];
        //設定前端點
        [_iFlySpeechRecognizer setParameter:instance.vadBos forKey:[IFlySpeechConstant VAD_BOS]];
        //網路等待時間
        [_iFlySpeechRecognizer setParameter:@"20000" forKey:[IFlySpeechConstant NET_TIMEOUT]];

        //設定取樣率,推薦使用16K
        [_iFlySpeechRecognizer setParameter:instance.sampleRate forKey:[IFlySpeechConstant SAMPLE_RATE]];

        if ([instance.language isEqualToString:[IATConfig chinese]]) {
            //設定語言
            [_iFlySpeechRecognizer setParameter:instance.language forKey:[IFlySpeechConstant LANGUAGE]];
            //設定方言
            [_iFlySpeechRecognizer setParameter:instance.accent forKey:[IFlySpeechConstant ACCENT]];
        }else if ([instance.language isEqualToString:[IATConfig english]]) {
            [_iFlySpeechRecognizer setParameter:instance.language forKey:[IFlySpeechConstant LANGUAGE]];
        }
        //設定是否返回標點符號
        [_iFlySpeechRecognizer setParameter:instance.dot forKey:[IFlySpeechConstant ASR_PTT]];
    }
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

// !!!: push介面的識別,點選事件
- (IBAction)voiceToText:(id)sender {
    [self.iflyRecognizerView start];
}

// !!!: 觸發語音識別類的點選事件
- (IBAction)voiceToTextWithoutUI:(id)sender {
    self.textView.text = @"";
    // 這個需要手動停止翻譯.
    [_iFlySpeechRecognizer cancel];

    //設定音訊來源為麥克風
    [_iFlySpeechRecognizer setParameter:IFLY_AUDIO_SOURCE_MIC forKey:@"audio_source"];

    //設定聽寫結果格式為json
    [_iFlySpeechRecognizer setParameter:@"json" forKey:[IFlySpeechConstant RESULT_TYPE]];

    //保存錄音檔案,儲存在sdk工作路徑中,如未設定工作路徑,則預設儲存在library/cache下
    [_iFlySpeechRecognizer setParameter:@"asr.pcm" forKey:[IFlySpeechConstant ASR_AUDIO_PATH]];

    [_iFlySpeechRecognizer setDelegate:self];

    [_iFlySpeechRecognizer startListening];
}

// !!!:實現代理方法
// !!!:注意有沒有s, 語音識別的結果回撥(帶介面的那個)
-(void)onResult:(NSArray *)resultArray isLast:(BOOL)isLast
{
    NSMutableString *result = [[NSMutableString alloc] init];
    NSDictionary *dic = [resultArray objectAtIndex:0];
    for (NSString *key in dic) {
        [result appendFormat:@"%@",key];
    }

    // 注意: 語音識別回撥返回結果是一個json格式字串, 解析起來比較麻煩, 但是我們只需要其中的字串部分, 這個過程訊飛也覺得麻煩, 就推出了一個工具類, 能將這個josn解析最終字串返回. 這也是前面匯入ISRDataHelper.h的作用.
    NSString * resu = [ISRDataHelper stringFromJson:result];
    self.textView.text = [NSString stringWithFormat:@"%@%@",self.textView.text,resu];
}
// !!!:解析失敗代理方法
-(void)onError:(IFlySpeechError *)error
{
    NSLog(@"解析失敗了");
}

// !!!:語音識別類的回撥方法(不帶介面的那個)
- (void) onResults:(NSArray *) results isLast:(BOOL)isLast{
    NSMutableString *result = [[NSMutableString alloc] init];
    NSDictionary *dic = [results objectAtIndex:0];
    for (NSString *key in dic) {
        [result appendFormat:@"%@",key];
    }
    NSString * resu = [ISRDataHelper stringFromJson:result];
    self.textView.text = [NSString stringWithFormat:@"%@%@", self.textView.text, resu];
}

@end

在上面的程式碼中, 使用了兩個類:

1
2
#import "ISRDataHelper.h"
#import "IATConfig.h"

他們的功能已經在註釋中說明, 那麼這兩個類的原始檔怎辦呢… 讓我想想…
算了, 最後我把這個工程傳到git上吧. 你們從哪裡扒下來就好了.

四.語音合成

其實iOS自帶語音合成, 我們不必使用訊飛也可以達到這樣的效果, 下面的程式碼能讓你的APP讀出這些文字.
Appdelegate.m中, 新增一個延展, 並且 didFinishLaunchingWithOptions 中新增如下程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@interface AppDelegate ()
@property(nonatomic,strong)AVSpeechSynthesizer * speechSynthesizer; // 合成器
@property(nonatomic,strong)AVSpeechUtterance * speechUtterance; // 合成器所說的內容
@end

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.

    self.speechSynthesizer = [[AVSpeechSynthesizer alloc] init];

    self.speechUtterance = [[AVSpeechUtterance alloc] initWithString:@"啪啪啪"];

    [self.speechSynthesizer  speakUtterance:self.speechUtterance];

    return YES;
}

執行之後, app能讀出”啪啪啪”. 女性發音效果更好.

  • 使用訊飛實現啪啪啪的功能
    我們直接在上面的工程裡新增吧.
    首先在sb中的viewcontroller裡, 再拖一個textfiled, 我們讓訊飛朗讀textfiled中的內容.
    file-listfile-list

viewController.m, 這個將三個功能寫到了同一個controller中.比較臃腫, 你們自己捋順一下, 封裝成類, 供以後使用.

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#import "ViewController.h"
#import <iflyMSC/iflyMSC.h>
#import "ISRDataHelper.h"
#import "IATConfig.h"

#import "PcmPlayerDelegate.h"
#import "PcmPlayer.h"
#import "TTSConfig.h"

typedef NS_OPTIONS(NSInteger, SynthesizeType) {
    NomalType           = 5,//普通合成
    UriType             = 6, //uri合成
};

@interface ViewController ()<IFlyRecognizerViewDelegate, IFlySpeechRecognizerDelegate, IFlySpeechRecognizerDelegate>
// 翻譯好的Text會展示在這個label上.
@property (weak, nonatomic) IBOutlet UILabel *textView;
// 朗讀這裡的內容.
@property (weak, nonatomic) IBOutlet UITextField *VoiceText;

/*!
 *  語音識別控制元件
 *    錄音時觸控控制元件結束錄音,開始識別(相當於舊版的停止);觸控其他位置,取消錄音,結束會話(取消)
 *  出錯時觸控控制元件,重新開啟會話(相當於舊版的再說一次);觸控其他位置,取消錄音,結束會話(取消)
 *
 */
@property (nonatomic,strong)IFlyRecognizerView * iflyRecognizerView;

/*!
 *  語音識別類
 *   此類現在設計為單例,你在使用中只需要建立此物件,不能呼叫release/dealloc函式去釋放此物件。所有關於語音識別的操作都在此類中。
 */
@property (nonatomic, strong)IFlySpeechRecognizer * iFlySpeechRecognizer;


@property (nonatomic, strong) IFlySpeechSynthesizer * iFlySpeechSynthesizer;//語音合成物件
@property (nonatomic, strong) PcmPlayer *audioPlayer;//用於播放音訊的
@property (nonatomic, assign) SynthesizeType synType;//是何種合成方式
@property (nonatomic, assign) BOOL hasError;//將解析過程中是否出現錯誤
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // 語音識別檢視空間及配置.
    [self initRecognizerView];

    // 語音識別類的初始化及配置.
    [self initSpeechRecognizer];

    // 初始化語音合成
    [self initMakeVoice];
}
// !!!:語音識別檢視空間及配置--方法.
-(void)initRecognizerView{
    _iflyRecognizerView = [[IFlyRecognizerView alloc] initWithCenter:self.view.center];
    _iflyRecognizerView.delegate = self;
    [_iflyRecognizerView setParameter: @"iat" forKey: [IFlySpeechConstant IFLY_DOMAIN]];
    //asr_audio_path保存錄音檔名,如不再需要,設定value為nil表示取消,預設目錄是documents
    [_iflyRecognizerView setParameter:@"asrview.pcm " forKey:[IFlySpeechConstant ASR_AUDIO_PATH]];
}
// !!!:語音識別類的初始化及配置--方法.
-(void)initSpeechRecognizer{
    //單例模式,無UI的例項
    if (_iFlySpeechRecognizer == nil) {
        _iFlySpeechRecognizer = [IFlySpeechRecognizer sharedInstance];

        [_iFlySpeechRecognizer setParameter:@"" forKey:[IFlySpeechConstant PARAMS]];

        //設定聽寫模式
        [_iFlySpeechRecognizer setParameter:@"iat" forKey:[IFlySpeechConstant IFLY_DOMAIN]];
    }
    _iFlySpeechRecognizer.delegate = self;

    if (_iFlySpeechRecognizer != nil) {
        IATConfig *instance = [IATConfig sharedInstance];

        //設定最長錄音時間
        [_iFlySpeechRecognizer setParameter:instance.speechTimeout forKey:[IFlySpeechConstant SPEECH_TIMEOUT]];
        //設定後端點
        [_iFlySpeechRecognizer setParameter:instance.vadEos forKey:[IFlySpeechConstant VAD_EOS]];
        //設定前端點
        [_iFlySpeechRecognizer setParameter:instance.vadBos forKey:[IFlySpeechConstant VAD_BOS]];
        //網路等待時間
        [_iFlySpeechRecognizer setParameter:@"20000" forKey:[IFlySpeechConstant NET_TIMEOUT]];

        //設定取樣率,推薦使用16K
        [_iFlySpeechRecognizer setParameter:instance.sampleRate forKey:[IFlySpeechConstant SAMPLE_RATE]];

        if ([instance.language isEqualToString:[IATConfig chinese]]) {
            //設定語言
            [_iFlySpeechRecognizer setParameter:instance.language forKey:[IFlySpeechConstant LANGUAGE]];
            //設定方言
            [_iFlySpeechRecognizer setParameter:instance.accent forKey:[IFlySpeechConstant ACCENT]];
        }else if ([instance.language isEqualToString:[IATConfig english]]) {
            [_iFlySpeechRecognizer setParameter:instance.language forKey:[IFlySpeechConstant LANGUAGE]];
        }
        //設定是否返回標點符號
        [_iFlySpeechRecognizer setParameter:instance.dot forKey:[IFlySpeechConstant ASR_PTT]];
    }
}

// !!!:語音合成的初始化
-(void)initMakeVoice{
    TTSConfig *instance = [TTSConfig sharedInstance];
    if (instance == nil) {
        return;
    }

    //合成服務單例
    if (_iFlySpeechSynthesizer == nil) {