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;

}

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐