iOS Speech框架详解及Objective-C使用示例
1. Speech框架概述
Speech框架是Apple提供的用于语音识别的框架,它允许开发者在iOS、iPadOS、macOS和visionOS应用中实现语音到文本的转换功能。该框架支持从预录制的音频文件或实时麦克风输入中识别语音,并提供转录结果、替代解释和置信度评分。
主要功能包括:
实时语音识别(从麦克风输入)
预录制音频文件的语音识别
支持多种语言和区域设置
提供识别结果的置信度评分
支持部分结果和最终结果的5
NSMicrophoneUsageDescription:如果需要使用麦克风,还需添加此键
3.2 请求授权
在使用任何Speech API之前,必须请求用户授权:
#import <Speech/Speech.h>
- (void)requestSpeechAuthorization {
[SFSpeechRecognizer requestAuthorization:^(SFSpeechRecognizerAuthorizationStatus authStatus) {
dispatch_async(dispatch_get_main_queue(), ^{
switch (authStatus) {
case SFSpeechRecognizerAuthorizationStatusAuthorized:
NSLog(@"语音识别已授权");
break;
case SFSpeechRecognizerAuthorizationStatusDenied:
NSLog(@"用户拒绝了语音识别授权");
break;
case SFSpeechRecognizerAuthorizationStatusRestricted:
NSLog(@"语音识别在本设备上受限");
break;
case SFSpeechRecognizerAuthorizationStatusNotDetermined:
NSLog(@"语音识别授权尚未确定");
break;
default:
break;
}
});
}];
}
4. Objective-C示例代码
4.1 从预录制文件识别语音
#import <Speech/Speech.h>
@interface SpeechRecognitionManager : NSObject
@property (nonatomic, strong) SFSpeechRecognitionTask *recognitionTask;
@property (nonatomic, strong) SFSpeechRecognizer *speechRecognizer;
- (void)recognizeSpeechFromURL:(NSURL *)audioURL;
- (void)cancelRecognition;
@end
@implementation SpeechRecognitionManager
- (instancetype)init {
self = [super init];
if (self) {
// 使用中文识别
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"zh-CN"];
self.speechRecognizer = [[SFSpeechRecognizer alloc] initWithLocale:locale];
}
return self;
}
- (void)recognizeSpeechFromURL:(NSURL *)audioURL {
// 检查语音识别服务是否可用
if (!self.speechRecognizer.isAvailable) {
NSLog(@"语音识别服务当前不可用");
return;
}
// 创建请求
SFSpeechURLRecognitionRequest *request = [[SFSpeechURLRecognitionRequest alloc] initWithURL:audioURL];
// 可选:设置任务提示,帮助提高识别准确率
request.taskHint = SFSpeechRecognitionTaskHintDictation;
// 开始识别
__weak typeof(self) weakSelf = self;
self.recognitionTask = [self.speechRecognizer recognitionTaskWithRequest:request resultHandler:^(SFSpeechRecognitionResult * _Nullable result, NSError * _Nullable error) {
__strong typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf) return;
if (!result) {
if (error) {
NSLog(@"识别错误: %@", error);
}
return;
}
// 检查是否为最终结果
if (result.isFinal) {
NSLog(@"最终识别结果: %@", result.bestTranscription.formattedString);
// 可以获取多个候选结果及其置信度
for (SFTranscription *transcription in result.transcriptions) {
NSLog(@"转录结果: %@", transcription.formattedString);
}
} else {
// 处理部分结果
NSLog(@"部分识别结果: %@", result.bestTranscription.formattedString);
}
}];
}
- (void)cancelRecognition {
[self.recognitionTask cancel];
self.recognitionTask = nil;
}
@end
4.2 从麦克风实时识别语音
#import <Speech/Speech.h>
#import <AVFoundation/AVFoundation.h>
@interface LiveSpeechRecognizer : NSObject <AVAudioRecorderDelegate>
@property (nonatomic, strong) SFSpeechRecognitionTask *recognitionTask;
@property (nonatomic, strong) SFSpeechRecognizer *speechRecognizer;
@property (nonatomic, strong) AVAudioEngine *audioEngine;
@property (nonatomic, strong) SFSpeechAudioBufferRecognitionRequest *recognitionRequest;
- (void)startLiveRecognitionWithCompletion:(void (^)(NSString *transcript))completion;
- (void)stopLiveRecognition;
@end
@implementation LiveSpeechRecognizer
- (instancetype)init {
self = [super init];
if (self) {
// 使用中文识别
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"zh-CN"];
self.speechRecognizer = [[SFSpeechRecognizer alloc] initWithLocale:locale];
}
return self;
}
- (void)startLiveRecognitionWithCompletion:(void (^)(NSString * _Nonnull))completion {
// 检查可用性
if (!self.speechRecognizer.isAvailable) {
NSLog(@"语音识别服务当前不可用");
return;
}
// 取消之前的任务
[self.recognitionTask cancel];
self.recognitionTask = nil;
NSError *error = nil;
// 配置音频会话
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryRecord mode:AVAudioSessionModeMeasurement options:AVAudioSessionCategoryOptionDuckOthers error:&error];
if (error) {
NSLog(@"设置音频会话类别失败: %@", error);
return;
}
[audioSession setActive:YES withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&error];
if (error) {
NSLog(@"激活音频会话失败: %@", error);
return;
}
// 创建音频引擎
self.audioEngine = [[AVAudioEngine alloc] init];
// 创建识别请求
self.recognitionRequest = [[SFSpeechAudioBufferRecognitionRequest alloc] init];
// 确保有音频引擎和识别请求
if (!self.audioEngine || !self.recognitionRequest) {
NSLog(@"无法创建音频引擎或识别请求");
return;
}
// 设置请求属性
self.recognitionRequest.shouldReportPartialResults = YES;
self.recognitionRequest.taskHint = SFSpeechRecognitionTaskHintDictation;
// 开始识别任务
__weak typeof(self) weakSelf = self;
self.recognitionTask = [self.speechRecognizer recognitionTaskWithRequest:self.recognitionRequest resultHandler:^(SFSpeechRecognitionResult * _Nullable result, NSError * _Nullable error) {
__strong typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf) return;
BOOL isFinal = NO;
if (result) {
// 处理识别结果
NSString *transcript = result.bestTranscription.formattedString;
if (completion) {
completion(transcript);
}
isFinal = result.isFinal;
}
if (error != nil || isFinal) {
// 结束识别
[strongSelf.audioEngine stop];
[[strongSelf.audioEngine inputNode] removeTapOnBus:0];
strongSelf.recognitionRequest = nil;
strongSelf.recognitionTask = nil;
}
}];
// 设置音频输入
AVAudioInputNode *inputNode = self.audioEngine.inputNode;
AVAudioFormat *recordingFormat = [inputNode outputFormatForBus:0];
// 添加音频输入到识别请求
[inputNode installTapOnBus:0 bufferSize:1024 format:recordingFormat block:^(AVAudioPCMBuffer * _Nonnull buffer, AVAudioTime * _Nonnull when) {
[weakSelf.recognitionRequest appendAudioPCMBuffer:buffer];
}];
// 准备并启动音频引擎
[self.audioEngine prepare];
[self.audioEngine startAndReturnError:&error];
if (error) {
NSLog(@"启动语音识别时出错: %@", error);
}
}
- (void)stopLiveRecognition {
[self.audioEngine stop];
[[self.audioEngine inputNode] removeTapOnBus:0];
[self.recognitionRequest endAudio];
[self.recognitionTask cancel];
self.recognitionRequest = nil;
self.recognitionTask = nil;
}
@end
5. 在ViewController中使用示例
#import "ViewController.h"
#import "SpeechRecognitionManager.h"
#import "LiveSpeechRecognizer.h"
@interface ViewController ()
@property (nonatomic, strong) SpeechRecognitionManager *fileRecognizer;
@property (nonatomic, strong) LiveSpeechRecognizer *liveRecognizer;
@property (weak, nonatomic) IBOutlet UITextView *transcriptTextView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化识别器
self.fileRecognizer = [[SpeechRecognitionManager alloc] init];
self.liveRecognizer = [[LiveSpeechRecognizer alloc] init];
// 请求授权
[self requestSpeechAuthorization];
}
- (void)requestSpeechAuthorization {
[SFSpeechRecognizer requestAuthorization:^(SFSpeechRecognizerAuthorizationStatus authStatus) {
dispatch_async(dispatch_get_main_queue(), ^{
switch (authStatus) {
case SFSpeechRecognizerAuthorizationStatusAuthorized:
NSLog(@"语音识别已授权");
break;
case SFSpeechRecognizerAuthorizationStatusDenied:
NSLog(@"用户拒绝了语音识别授权");
break;
case SFSpeechRecognizerAuthorizationStatusRestricted:
NSLog(@"语音识别在本设备上受限");
break;
case SFSpeechRecognizerAuthorizationStatusNotDetermined:
NSLog(@"语音识别授权尚未确定");
break;
default:
break;
}
});
}];
}
// 按钮操作 - 从文件识别
- (IBAction)recognizeFromFile:(id)sender {
// 假设我们有一个音频文件URL
NSURL *audioURL = [NSURL fileURLWithPath:@"/path/to/audio/file.m4a"];
[self.fileRecognizer recognizeSpeechFromURL:audioURL];
}
// 按钮操作 - 开始实时识别
- (IBAction)startLiveRecognition:(id)sender {
__weak typeof(self) weakSelf = self;
[self.liveRecognizer startLiveRecognitionWithCompletion:^(NSString *transcript) {
dispatch_async(dispatch_get_main_queue(), ^{
weakSelf.transcriptTextView.text = transcript;
});
}];
}
// 按钮操作 - 停止实时识别
- (IBAction)stopLiveRecognition:(id)sender {
[self.liveRecognizer stopLiveRecognition];
}
@end
6. 最佳实践
6.1 优化用户体验
清晰的权限说明:在Info.plist中提供明确、具体的权限描述
处理服务不可用情况:检查isAvailable属性并提供适当的反馈
显示识别状态:向用户提供实时反馈,显示识别进度和结果
限制识别时长:对于长语音,考虑分段处理
6.2 性能优化
停止不必要的识别:在不需要时取消识别任务
使用适当的任务提示:设置taskHint可以提高特定场景下的识别准确率
考虑网络要求:某些语言需要网络连接才能进行识别
6.3 错误处理
正确处理授权失败情况
处理语音识别服务暂时不可用的情况
实现SFSpeechRecognizerDelegate来监听可用性变化
7. 平台兼容性
iOS 10.0+
iPadOS 10.0+
macOS 10.15+
Mac Catalyst 13.1+
visionOS 1.0+ 通过合理使用Speech框架,你可以为应用添加强大的语音识别功能,提升用户体验并拓展应用的交互方式。
8. 其他注意事项
8.1 识别标点符号
// 创建识别请求
_recognitionRequest = [[SFSpeechAudioBufferRecognitionRequest alloc] init];
_recognitionRequest.shouldReportPartialResults = YES;
// 设置任务提示为听写模式,这对于标点符号识别非常重要
_recognitionRequest.taskHint = SFSpeechRecognitionTaskHintDictation;
// 确保使用服务器端识别以获得更好的标点符号支持
if (@available(iOS 13.0, *)) {
_recognitionRequest.requiresOnDeviceRecognition = NO;
}
// 对于所有iOS版本,启用部分结果报告,确保实时标点反馈
_recognitionRequest.shouldReportPartialResults = YES;
// 对于iOS 16及以上版本,可以设置更明确的标点符号配置
if (@available(iOS 16.0, *)) {
_recognitionRequest.addsPunctuation = YES;
}
- (void)startRecording {
// 停止之前可能正在运行的任务
if (_recognitionTask) {
[_recognitionTask cancel];
_recognitionTask = nil;
}
// 设置音频会话
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *error;
[audioSession setCategory:AVAudioSessionCategoryRecord error:&error];
[audioSession setActive:YES error:&error];
if (error) {
NSLog(@"音频会话设置错误: %@", error.localizedDescription);
return;
}
// 创建录音文件URL
[self setupAudioFileURL];
// 设置录音器参数
NSDictionary *recordSettings = @{
AVFormatIDKey: @(kAudioFormatMPEG4AAC),
AVSampleRateKey: @44100.0,
AVNumberOfChannelsKey: @1,
AVEncoderAudioQualityKey: @(AVAudioQualityHigh)
};
// 初始化音频录音器
_audioRecorder = [[AVAudioRecorder alloc] initWithURL:_audioFileURL settings:recordSettings error:&error];
if (!_audioRecorder || error) {
NSLog(@"音频录音器初始化失败: %@", error.localizedDescription);
return;
}
// 准备并开始录音
_audioRecorder.meteringEnabled = YES;
[_audioRecorder prepareToRecord];
[_audioRecorder record];
NSLog(@"录音开始,文件保存在: %@", _audioFileURL.path);
// 创建识别请求
_recognitionRequest = [[SFSpeechAudioBufferRecognitionRequest alloc] init];
_recognitionRequest.shouldReportPartialResults = YES;
// 设置任务提示为听写模式,这对于标点符号识别非常重要
_recognitionRequest.taskHint = SFSpeechRecognitionTaskHintDictation;
// 确保使用服务器端识别以获得更好的标点符号支持
if (@available(iOS 13.0, *)) {
_recognitionRequest.requiresOnDeviceRecognition = NO;
}
// 对于所有iOS版本,启用部分结果报告,确保实时标点反馈
_recognitionRequest.shouldReportPartialResults = YES;
// 对于iOS 16及以上版本,可以设置更明确的标点符号配置
if (@available(iOS 16.0, *)) {
_recognitionRequest.addsPunctuation = YES;
}
// 获取音频输入
AVAudioInputNode *inputNode = _audioEngine.inputNode;
// 修复installTapOnBus方法调用语法
[inputNode installTapOnBus:0 bufferSize:1024 format:[inputNode inputFormatForBus:0] block:^(AVAudioPCMBuffer * _Nonnull buffer, AVAudioTime * _Nonnull when) {
[self.recognitionRequest appendAudioPCMBuffer:buffer];
// 提取音频样本并更新波形图
if (buffer.floatChannelData) {
float *samples = buffer.floatChannelData[0];
[self.waveformView addSamples:samples count:buffer.frameLength];
}
}];
// 启动音频引擎
[_audioEngine prepare];
[_audioEngine startAndReturnError:&error];
if (error) {
NSLog(@"音频引擎启动错误: %@", error.localizedDescription);
return;
}
// 开始语音识别任务
__weak typeof(self) weakSelf = self;
_recognitionTask = [_speechRecognizer recognitionTaskWithRequest:_recognitionRequest resultHandler:^(SFSpeechRecognitionResult * _Nullable result, NSError * _Nullable error) {
BOOL isFinal = NO;
if (result) {
// 获取原始识别文本
NSString *recognizedText = result.bestTranscription.formattedString;
// 对识别结果进行后处理,添加或优化标点符号
NSString *processedText = [weakSelf addPunctuationIfNeeded:recognizedText];
// 更新识别文本
weakSelf.recognizedTextLabel.text = processedText;
isFinal = result.isFinal;
}
if (error || isFinal) {
[weakSelf.audioEngine stop];
[inputNode removeTapOnBus:0];
weakSelf.recognitionRequest = nil;
weakSelf.recognitionTask = nil;
}
}];
// 更新UI状态
[self.waveformView clear]; // 清空之前的波形
_sendButton.enabled = YES;
}
更多推荐
所有评论(0)